跳转至

绘制循环

让我们直接进入 Direct3D 工作原理的速成课。在 Direct3D 游戏的源代码中,你会发现一个处理输入和渲染图形的无限循环。这个绘制循环中的每次迭代称为一帧。如果我们删掉所有无关代码,只关注一个空骨架,我们可以用以下代码可视化游戏的主循环:

int WINAPI WinMain(args)
{
    /* 这里会调用一些代码
    设置 Direct3D 并初始化游戏。
    为了简洁省略。 */
    MSG msg;
    while(TRUE) {
        /* 这里会放处理传入
        鼠标和键盘消息的代码。 */
        drawFrame(); // 这是我们关心的函数
    }
    /* 这里会调用一些代码
    在退出前清理一切。 */
}

这个函数是游戏的入口点。简单来说,它初始化游戏,然后进入游戏的主循环。在主循环内,它执行负责处理用户输入的代码,然后调用 drawFrame() 用 Direct3D 重绘屏幕。(看看 GameHackingExamples/Chapter8_Direct3DApplication 代码,查看一个功能完整的游戏循环。)

每次调用时,drawFrame() 函数都会重绘整个屏幕。代码看起来像这样:

void drawFrame()
{
  device->Clear(0, NULL, D3DCLEAR_TARGET, 
D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    device->BeginScene();
    // 绘图会在这里发生
    device->EndScene();
    device->Present(NULL, NULL, NULL, NULL);
}

用 device->Clear①清除屏幕后,drawFrame() 函数调用 device->BeginScene() 解锁场景进行绘制。然后它执行一些绘图代码(绘图代码实际做什么现在不重要),并用 device->EndScene() 调用锁定场景。最后,它通过调用 device->Present() 函数把场景渲染到屏幕。

注意所有这些函数都是作为名为 device 的某个实例的成员调用的。这只是表示 Direct3D 设备的对象实例,用来调用各种绘图调用。另外,注意这个函数没有任何实际绘图代码,但这没关系。真正重要的是你理解绘制循环、帧和 Direct3D 设备的高层概念。回顾一下,游戏有一个有两个职责的主循环:

(cid:127)处理传入消息

(cid:127)把游戏绘制到屏幕

这个循环中的每次迭代称为一帧,每帧由设备绘制。控制设备让你访问游戏状态最敏感和最详细的细节;也就是说,你将能够在数据被解析、处理和渲染到屏幕后窥探游戏状态。此外,你将能够修改这个状态的输出。这两个超能力让你能实现各种令人惊叹的破解。