最近在学习《3D 游戏编程大师技巧》第 3 章,摘抄一些我认为重要的信息,并添加上自己的一些思考。
本章将建立一个基于软件接口的虚拟计算机系统,它将支持线性 8/16 位帧缓存(双缓存)/输入设备以及声音和音乐处理功能。有了这个接口,本书剩余部分便可以将重点放在 3D 数学、图形和游戏编程上。
只要能够有一个支持输入、声音和音乐的双缓存、线性寻址图形系统通信,谁会在乎它的工作原理呢?我们的兴趣在于 3D 图形编程,而不是低级设置。由于我们 99% 的工作都与光栅化、纹理映射、光照、隐藏面消除等相关,所以这种方法是可行的。
创建一台通用虚拟计算机,再在上面进行 3D 图形试验和编写游戏。
我的最低底线是,要移植 3D 内容,只要能够创建双缓存显示,一切都将正常工作。
请牢记,只要理解了我提供的 API,将不需要了解有关 Win32 和 DirectX 的任何知识,因为我们对 3D 图形所做的任何处理都是由这些 API、帧缓存和 C/C++ 代码组成的。
虚拟计算机的主旨在于帮助我们将精力集中在 3D 而不是(Win32 和 DirectX)设置细节上。
我过去常说:“只要能够绘制像素和读取键盘输入,就能够编写 DOOM。” 的确是这样,您只需能够访问帧缓存,其它一切都很简单。由于本书主要介绍软件 3D 编程和光栅化,因此所有算法都没有使用任何形式的 3D 加速。我们将绘制多边形,进行光照计算,对帧缓存中的每个像素进行其它处理。
注意:您可能会说,在可以使用硬件的情况下,为什么使用软件呢?原因有 3 个:首先,要成为优秀的图形编程人员,必须知道如何自动动手来完成各项任务;其次,了解工作原理有助于理解硬件加速;最后,谁来编写硬件代码呢?
我们将设计这样一个系统,即基于一个可见的主显示缓存和一个不可见的离屏辅显示缓存。这两个缓存都应该是可线性寻址的。
这是一个非常有用的练习。基本上,我们设计一个可移植的图形系统。彩这些思想,将能够设计出这样的游戏引擎,即通过实现少数几个函数的“内部细节”,就能够将它移植到其它平台。
我们将逐层建立游戏控制台,直到实现虚拟计算机。首先我们将从方程中消除 Windows。
第一版本的游戏控制台应执行下列任务:
- 打开窗口。
- 调用用户定义的初始化函数 Game_Init()。
- 进入到主 Windows 事件循环,处理消息,然后返回。
- 调用用户定义的主工作函数 Game_Main(),它执行一次游戏逻辑循环,然后返回。
- 回到第 3 步,直到用户关闭应用程序。
- 调用用户定义的关闭函数 Game_Shutdown(),它执行清除工作。
如果能够实现一个执行步骤 1~6 中功能的外壳程序,则完全不用考虑 Windows,从而将精力集中在函数 Game_Init()、Game_Main()、Game_Shutdown() 上。
我们在本书第 2 章学习笔记代码的基础上,构造第一版本的游戏控制台。
代码如下:
|
|
(上面的代码)创建一个窗口,调用 Game_Init(),在每次事件循环中调用 Game_Main()。最后,用户关闭该窗口时,将调用 Game_Shutdown()。
您只需要将全部游戏功能加入到这 3 个函数中,仅此而已——我们已经将一个 Windows 应用程序抽象成 3 个函数调用,其它工作都已经完成。
现在需要 DirectX 接口来模拟虚拟计算机,并提供基本的双缓存图形系统以及声音和输入功能。
如图 3.9 所示,T3DLIB1 是一个相当简单的 2D 引擎。实质上,它是一个 2D、8/16 位颜色、双缓存的 DirectX 引擎,支持任何分辨率和裁剪。该引擎还支持窗口模式和全屏模式,并负责所有设置工作。因此,不管是在窗口模式下还是在全屏模式下,都可以将像素写入辅助缓存,程序逻辑负责将辅助缓存复制到主缓存的处理细节。
现在我们利用作者编写的 T3DLIB 创建第二版本的游戏控制台。首先我们需要:
- 把 t3dlib1.h、t3dlib2.h、t3dlib3.h 添加到工程的 \Header Files 目录
- 把 t3dlib1.lib、t3dlib2.lib、t3dlib3.lib 添加到工程的 \Source Files 目录
工程目录见下图:
然后在 Project Properties -> Configuration Properties -> VC++ Directores -> Include Directores 和 Library Directores 添加 DirectX9 的对应路径,其中 \DirectX9 是从本书光盘资源的 \DirectX\dx9sdkcp.exe 直接解压而来。见下图:
然后在 Project Properties -> Configuration Properties -> Linker -> Input -> Additional Dependencies 添加:
- ddraw.lib
- dsound.lib
- dinput.lib
- dinput8.lib
- winmm.lib
接下来就是第二版本控制台的完整代码:
|
|
运行程序,编译器报错(一):
c:\program files (x86)\windows kits\8.1\include\um\winnt.h(342): error C2146: syntax error : missing ';' before identifier 'PVOID64'
查阅相关资料,出现这个错误的原因是,PVOID64 是一个宏,在 64 位编译下起作用,包含在(我本机的是)c:\program files (x86)\windows kits\8.1\include\shared\basetsd.h 中。但因为我们上面在 VC++ Directores 的 Include Directores 里包含了 dx9 的 \Include 目录,里面也有一个 basetsd.h,但这个 basetsd.h 里没有 PVOID64 的定义,且编译时优先使用这个目录的 basetsd.h,所以找不到 PVOID64,故而报错。
解决办法有:
办法一:把 dx9 的 \Include 的优先级下移,使编译器优先使用 c:\program files (x86)\windows kits\8.1\include\shared\basetsd.h。
办法二:改写 winnt.h 代码,在
typedef void *PVOID;
typedef void *POINTER_64 PVOID64;
之前增加一行:
#define POINTER_64 __ptr64
不过最好不要轻易改写 winnt.h。
办法三:移除 dx9 \Include 目录里的 basetsd.h(我使用这个)。
继续运行程序,编译器报错(二):
error C2065: 'index' : undeclared identifier
位置出现在 t3dlib1.cpp 的 Load_Animation_BOB 函数,见下图:
// set the end of the list to a -1
bob->animations[anim_index][index] = -1;
在这行代码上面的 for 循环,使用 index 作为下标进行遍历,在离开 for 循环后,index 应该是指向 bob->animations[anim_index] 的末尾元素,所以只要把 index 放在 for 外面即可:
// load data into
int index;
for (index=0; index<num_frames; index++)
bob->animations[anim_index][index] = sequence[index];
继续运行程序,编译器报错(三):
t3dlib2.obj : error LNK2019: unresolved external symbol _DirectInput8Create@20 referenced in function "int __cdecl DInput_Init(void)" (?DInput_Init@@YAHXZ)
只要在 Project Properties -> Configuration Properties -> Linker -> Input -> Additional Dependencies 里加上 dinput8.lib 即可。
继续运行程序,成功显示游戏控制台窗口:
由于窗口显示使用 16 色,所以看起来跟常规的 Windows 窗口不太一样。
应确保选择窗口模式时,窗口的客户区域(client-size)为 window_width * window_height
本章在《Windows 游戏编程大师技巧》和本书之间搭建了一座桥梁。您应该知道,3D 图形学研究的不是 DirectX 和 Windows,而是数学、数学还是数学。为进行 3D 图形编程,只需要一个包含一些函数和两个帧缓存的平台。