65.9K
CodeProject 正在变化。 阅读更多。
Home

原生 Win32 API OpenGL 教程 - 第 1 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.37/5 (7投票s)

2016 年 4 月 17 日

CPOL

5分钟阅读

viewsIcon

28562

downloadIcon

4068

原生 Win32 API OpenGL 教程 - 第 1 部分

引言

Code Project 论坛上一些用户提出的问题让我意识到,互联网上大多数 OpenGL 教程都已过时。许多教程都引用了旧的 NeHe 教程,而这些教程自 Jeff Molofee 将其网站的维护工作转交给 GameDev 后就再未更新。

自那时起,Visual Studio 2013 和 2015 已发布,Windows 7、8 和 10 也已发布。因此,似乎是时候至少尝试创建一些符合这些变化的入门教程了。

背景

我们的目标很简单——在 OpenGL 窗口中创建一个标准的旋转位图立方体。

自 Windows 98 以来的所有 Windows 版本都原生支持 OpenGL,你无需添加或进行任何操作。Windows 7、8 和 10 均自带 OpenGL 版本 3.3.0,但一些显卡制造商可能会通过其驱动程序安装将版本提升至 4.5。但你可以假定,任何当前的 Windows 机器上至少都有 OpenGL 版本 3.3。

人们尝试使用 NeHe 教程时遇到的第一个问题是 GLAUX 库已被弃用,在 Visual Studio 中或作为 Windows 的 DLL 都不再存在。原因是这些函数很容易被标准的 Win32 API 调用替代,在本系列文章中,我们将采用这种方法。我经常看到关于下载 GLAUX 的头文件和编译好的 DLL 的评论,但我们会避免这样做,因为它没有未来。

我将尝试采用 KISS(Keep It Simple Stupid,保持简单愚蠢)的方法,这种方法的一部分就是编程代码不包含对象或框架。这并非我不相信这类事物有价值,而是为了使代码尽可能简单,并服务于更广泛的受众。

代码使用了标准的 `<TChar.h>` 接口,因此可以在 Ansi、Unicode 和 Wide 字符模式下编译。我曾犹豫是否要这样做以遵循 KISS 原则,但觉得不这样做会限制编译模式和选项,尤其对非英语国家而言。由于只涉及将“char”类型替换为“TCHAR”以及在文本字符串周围添加一些 `_T` 语句,我认为这在理解上几乎没有损失。

Using the Code

首先,让我们看一下我们将使用的 OpenGL 窗口系统的伪代码,我们将这样使用它:

1.) Initialize OpenGL (Called Once)
2.) Scale the OpenGL viewPort (Initial Call)
repeat
3.) Draw the scene
4.) Transfer the scene to screen
until window closes

** Note Item 2) the scale process also gets called if the window changes size 

采用这种方法的原因是它将使 OpenGL 部分更具灵活性,包括迁移到 MFC、WPF 等框架。

我使用的另一个概念是将所有 OpenGL 数据放入一个简单的结构体中,该结构体作为属性附加到窗口本身。在第一个教程中,人们可能会想为什么还要费力做这件事,因为使用普通的全局变量会很容易。原因在第二课中将变得显而易见,届时我们将有多个 OpenGL 窗口,然后在后续课程中会有更高级的多个视口。

这是第一个教程的 OpenGLData。渲染上下文将在未来的课程中始终存在,但所有其他数据将根据需要而变化。在我们的第一个教程中,我们将加载一个位图作为纹理(结构体中的 `gltexture`),将其放在立方体的侧面并旋转它(使用 `xrot`、`yrot` 进行旋转)。

typedef struct OpenGLData {
    HGLRC Rc;                                   // Our render context ** Always Needed
    GLuint glTexture;                           // Our texture to draw
    GLfloat    xrot;                            // X Rotation
    GLfloat    yrot;                            // Y Rotation
} GLDATABASE;code blocks look like this

这个基本数据结构通过一个 `static` 定义的标签 `string` 使用 `SetProp` API 调用附加到窗口句柄,并在需要时通过 `GetProp` 调用检索。

在本教程中我们使用的标签属性是:

static const TCHAR* DATABASE_PROPERTY = _T("OurDataStructure");

这是创建和附加数据结构到窗口句柄的代码,我们使用:

GLDATABASE* db = (GLDATABASE*) malloc(sizeof(GLDATABASE)); // Allocate structure
db->Rc = InitGL(Wnd);                                      // Initialize OpenGL hold render context
db->glTexture = 0;                                         // Zero the texture
db->xrot = 0.0f;                                           // Zero x rotation
db->yrot = 0.0f;                                           // Zero y rotation
SetProp(Wnd, DATABASE_PROPERTY, (HANDLE) db);              // Data structure to window property

任何时候,我们想访问数据,我们做一个简单的检索调用,如下所示:

GLDATABASE* db = (GLDATABASE*) GetProp(Wnd, DATABASE_PROPERTY); // Fetch the data structure

说了这么多,我们在本课中得到的是一个标准的窗口应用程序框架。当应用程序窗口创建时,`WM_CREATE` 将用于初始化 OpenGL 系统。返回的 `Render` 上下文将保存在我们的数据结构中。紧接着调用 `ReSizeGLScene` 来将 OpenGL 系统的初始大小设置为创建的窗口大小。

对于一个正常运行的窗口,我们只需要处理窗口的 `WM_PAINT` 消息,其代码如下:

case WM_PAINT: {                                                   // WM_PAINT MESSAGE
   PAINTSTRUCT Ps;
   GLDATABASE* db = (GLDATABASE*) GetProp(Wnd, DATABASE_PROPERTY); // Fetch the data base
   BeginPaint(Wnd, &Ps);                                           // Begin paint
   DrawGLScene(db, Ps.hdc);                                        // Draw the OpenGL scene
   SwapBuffers(Ps.hdc);                                            // Swap buffers
   EndPaint(Wnd, &Ps);                                             // End paint
   return 0;
}

它以正常的 `BeginPaint` 调用开始,然后调用 OpenGL 将其场景绘制到其渲染上下文中。返回后,我们将渲染上下文交换到 `PaintStructure` 设备上下文中,这将 OpenGL 场景绘制到我们的窗口上。然后我们进行清理并退出。

这里的诀窍是渲染过程不是持续运行的,它只在提供 `WM_PAINT` 消息时发生,所以如果你想动画化某些内容,在更改后你需要提供 `WM_PAINT` 消息。当我们激活计时器时,它会改变旋转并使窗口无效,这正是创建 `WM_PAINT` 消息以使窗口重绘的操作。

这个过程非常适合构建 3D 对象编辑器之类的东西,但不适合需要快速连续场景渲染的快节奏游戏。在后续课程中,我们将处理这个问题并使用线程。

现在,运行程序,然后使用文件菜单选择加载位图,执行此操作后,您将看到类似以下内容:

现在启动计时器,你的立方体应该开始旋转,这样本课就完成了。好了,这是我们的第一个 OpenGL 教程,在标准的应用程序窗口中运行 OpenGL。

在下一课中,我们将通过一个 MDI 应用程序来进一步深入。

历史

  • 版本 1.0:代码首次发布
© . All rights reserved.