1.5 Sample Application: Initializing Direct3D 在本章的例程中,初始化了一个D3D应用程序并用黑色填充显示窗口
图1.7 本书所有的应用程序都包含了d3dUtility.h和d3dUtility.cpp这两个文件,它们所包含的函数实现了所 有D3D应用程序都要去做的一些常见的功能。例如:创建一个窗口、初始化D3D、进入程序的消息 循环等。将这些功能封装在函数中能使示例程序更加突出该章的主题。另外,在我们学习本书的过 程中还会在这两个文件中加上一些通用的代码。 1.5.1 d3dUtility.h/cpp 在开始本章的例程之前,让我们先熟悉一下d3dUtility.h/cpp所提供的函数。d3dUtility.h如下:
// Include the main Direct3DX header file. This will include the // other Direct3D header files we need. #include <d3dx9.h>
namespace d3d { bool InitD3D( HINSTANCE hInstance, // [in] Application instance. int width, int height, // [in] Back buffer dimensions. bool windowed, // [in] Windowed (true)or // full screen (false). D3DDEVTYPE deviceType, // [in] HAL or REF IDirect3DDevice9** device); // [out] The created device.
int EnterMsgLoop( bool (*ptr_display)(float timeDelta)); LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
template<class T> void Release(T t) { if( t ) { t->Release(); t = 0; } }
template<class T> void Delete(T t) { if( t ) { delete t; t = 0; } } }
InitD3D——初始化一个应用程序主窗口并进行D3D的初始化。如果成功,则输出IDirect3DDevice9接口指针。从它的参数我们可以发现,我们能够设置窗口的大小和以窗口模式运行还是全屏模式运行。要知道它实现的细节,请看示例代码 EnterMsgLoop——这个函数封装了应用程序的消息循环。它需要输入一个显示函数的函数指针,显示函数为程序中绘制图形的代码块,这样做是为了使显示函数能够在空闲的时候被调用并显示场景,它的实现如下:
int d3d::EnterMsgLoop( bool (*ptr_display)(float timeDelta) ) { MSG msg; ::ZeroMemory(&msg, sizeof(MSG)); static float lastTime = (float)timeGetTime(); while(msg.message != WM_QUIT) { if(: eekMessage(&msg, 0, 0, 0,  M_REMOVE)) { ::TranslateMessage(&msg); : ispatchMessage(&msg); } else { float currTime = (float)timeGetTime(); float timeDelta = (currTime - lastTime)*0.001f; ptr_display(timeDelta); // call display function lastTime = currTime; } } return msg.wParam; }
与“time”有关的代码用于计算每次调用显示函数的时间间隔,即是每帧的时间。 Release——这个模函数能方便的释放COM接口并将它们的值设为NULL Delete——这个模函数能方便的删除一个对象并将指向其的指针设为NULL WndProc——应用程序主窗口的回调函数 1.5.2 Sample Framework 通过示例程序的框架,我们形成了一种通用的方法去构造本书的示例程序。每一个例程都有三个函数的实现,当然这不包括回调函数和WinMain主函数。这三个函数的实现不同,那么示例程序也就不同。这三个函数是: bool Setup()——在这个函数里,我们将准备一切该程序需要用到的东西,包括资源的分配,检查设备技术特性,设置应用程序的状态 void Clearup()——这个函数将释放Setup()中分配的资源 bool Display(float timeDelta)——顾名思义,所有与我们绘图和显示有关的代码都在这个函数里实现。参数timeDelta为每一帧的间隔时间,用来控制每秒的帧数。 1.5.3 Sample: D3D Init 这个示例程序将创建并初始化一个D3D应用程序,并用黑色填充屏幕。注意,我们使用了通用函数简化了初始化过程。 首先,我们要包含d3dUtility.h头文件,并为设备声明一个全局变量: #include "d3dUtility.h" IDirect3DDevice9* Device = 0; 然后实现我们的框架函数: bool Setup() { return true; } void Cleanup() { } 在这个程序中,我们不需要使用任何资源或触发任何事件,所以这两个函数都为空。
bool Display(float timeDelta) { if( Device ) { Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0); Device->resent(0, 0, 0, 0);// present backbuffer } return true; } Display方法调用了IDirect3DDevice::Clear方法分别用黑色和1.0填充后备表面和深度/模版缓冲。如果应用程序不停止的话,我们会一直执行这个操作。 IDirect3DDevice::Clear声明如下: HRESULT IDirect3DDevice9::Clear( DWORD Count, const D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil ); Count——pRects组中的矩形的个数 pRects——将要清除的屏幕矩形的数组,这使我们可以清除屏幕的某一部分 Flags——指定在哪些表面上执行这个操作 D3DCLEAR_TARGET——目的表面,通常为后备表面 D3DCLEAR_ZBUFFER——深度缓冲 D3DCLEAR_STENCIL——模版缓冲 Color——使用什么颜色填充清除的表面 Z——设置深度缓冲的值 Stencil——设置模版缓冲的值 屏幕被填充后,要调用IDirecte3DDevice9:resent方法进行后备表面的交换。 Windows 回调函数为一组事件集,使我们可用在按ESC键后退出程序。
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam,LPARAM lParam) { switch( msg ) { case WM_DESTROY: : ostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) : estroyWindow(hwnd); break; } return : efWindowProc(hwnd, msg, wParam, lParam); } 最后,WinMain按如下步骤运行: 1. 初始化主显示窗口和D3D 2. 调用Setup进行程序的准备工作 3. 使用Display函数作为参数进入消息循环 4. 清除应用程序最后释放IDirecte3DDevice9对象 int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance,800, 600, true, D3DDEVTYPE_HAL, &Device)) { ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) { ::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); return 0; }
本书的大部分程序,都是通过这三个函数的实现的。 特别注意:不要忘了在你的工程中加入d3d9.lib、d3dx9.lib、winmm.lib 这三个库!
1.6 Summary 1.D3D可以看作是应用程序和图形设备之间的中介。程序员通过调用D3D函数对物理硬件直接进行操作,当然这些都要基于设备的HAL 2.REF使开发者们能够测试那些D3D支持但显卡不支持的特性。 3.组件对象模型(COM)使D3D能独立于编程语言并具有向下兼容性。D3D程序员不需要知道COM的工作细节,他们只要知道怎样获得和释放一个COM接口就可以了。 4.表面是一种特殊的D3D接口,它用于存储2D图像。表面的像素格式用D3DFORMAT枚举类型的一个成员设定。表面和其它的D3D资源可以被存储在不同的内存池中,这要通过D3DPOOL枚举类型来设定。此外,表面还可以打开全屏抗锯齿功能来创建更平滑的图像。 5.IDirect3D9接口用来查询设备信息,并且用它来创建IDirect3DDevice9接口。 6.IDirect3DDevice9接口可以被认为是控制图形设备的软件接口。例如:调用IDirect3DDevice9::Clear方法将直接操作图形设备清空指定的表面。 7.样例框架为本书所有的程序提供了一致的接口。d3dUtility.h/cpp文件中封装的通用代码是每一个应用程序都要用到的初始化代码。通过封装这些代码,我们将其隐藏起来,使应用程序更能突出当前的主题。
|