初始化Direct3D(4)
1.5初始化Direct3D实例
在本例程中,初始化了一个Direct3D应用程序并用黑色填充显示窗口(如图1.7)。
图1.7
所有的应用程序都包含了d3dUtility.h和d3dUtility.cpp这两个文件,它们所包含的函数实现了所有Direct3D应用程序都要去做的一些常见的功能。例如:创建一个窗口、初始化Direct3D、进入程序的消息循环等。
1.5.1d3dUtility.h/cpp
让我们先熟悉一下d3dUtility.h/cpp所提供的函数。d3dUtility.h如下:
#include <d3dx9.h>
template<typename T>
void safe_release(T obj)
{
if(obj == NULL)
return;
obj->Release();
obj = NULL;
}
template<typename T>
void safe_delete(T obj)
{
if(obj == NULL)
return;
delete obj;
obj = NULL;
}
///////////////////////////////////////////////////////////////////////////////////
typedef bool (*DISPLAY_FUNC_PTR)(float timeDelta);
bool init_d3d(HINSTANCE instance, // application instance
int width, int height, // backbuffer dimensions
bool is_window, // true - windowed mode, false - full screen mode.
D3DDEVTYPE device_type, // HAL or REF
IDirect3DDevice9** device); // the create device
int enter_msg_loop(DISPLAY_FUNC_PTR display);
LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
init_d3d——初始化一个应用程序主窗口并进行Direct3D的初始化。如果成功,则输出IDirect3DDevice9接口指针。从它的参数我们可以发现,我们能够设置窗口的大小和以窗口模式运行还是全屏模式运行。要知道它实现的细节,请看示例代码。
//-----------------------------------------------------------------------
// Initialize windows and direct 3D.
//-----------------------------------------------------------------------
bool init_d3d(HINSTANCE instance, // application instance
int width, int height, // backbuffer dimensions
bool is_window, // true - windowed mode, false - full screen mode.
D3DDEVTYPE device_type, // HAL or REF
IDirect3DDevice9** device) // the create device
{
const char* classname = "Direct3D9App";
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = wnd_proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = classname;
if(! RegisterClass(&wc))
{
MessageBox(NULL, "RegisterClass() - failed.", NULL, MB_OK);
return false;
}
HWND hwnd = CreateWindow(classname, "Direct3D9App", WS_EX_TOPMOST,
0, 0, width, height, NULL, NULL, instance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "CreateWindow() - failed.", NULL, MB_OK);
return false;
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// initialize D3D
// step 1: Create the IDirect3D9 object.
IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
if(d3d9 == NULL)
{
MessageBox(NULL, "Direct3DCreate9() - failed.", NULL, MB_OK);
return false;
}
// step 2: check for hardware vertex presentation.
D3DCAPS9 caps;
d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, device_type, &caps);
int vp = 0;
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// step 3: fill out the D3DPRESENT_PARAMETERS structure.
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = is_window;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
// step 4: create the device.
if(FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, device_type, hwnd, vp, &d3dpp, device)))
{
// try again using a 16-bit depth buffer
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
if(FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, device_type, hwnd, vp, &d3dpp, device)))
{
d3d9->Release(); // done with d3d9 object
MessageBox(NULL, "CreateDevice() - failed.", NULL, MB_OK);
return false;
}
}
d3d9->Release(); // done with d3d9 object
return true;
}
enter_msg_loop——这个函数封装了应用程序的消息循环。它需要输入一个显示函数的函数指针,显示函数为程序中绘制图形的代码块,这样做是为了使显示函数能够在空闲的时候被调用并显示场景,它的实现如下:
//-----------------------------------------------------------------------
// Enter windows message loop and render game frames if there is no message
// comes from thread message queue.
//-----------------------------------------------------------------------
int enter_msg_loop(DISPLAY_FUNC_PTR display)
{
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
// The timeGetTime function retrieves the system time, in milliseconds.
// The system time is the time elapsed since Windows was started.
static float last_time = (float) timeGetTime();
while(msg.message != WM_QUIT)
{
// The PeekMessage function dispatches incoming sent messages, checks the thread message queue for a
// posted message, and retrieves the message (if any exist).
//
// If a message is available, the return value is nonzero.
// If no messages are available, the return value is zero.
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
float curr_time = (float) timeGetTime();
float time_delta = (curr_time - last_time) * 0.001f;
display(time_delta);
last_time = curr_time;
}
}
return (int) msg.wParam;
}
与“time”有关的代码用于计算每次调用显示函数的时间间隔,即是每帧的时间。
safe_release——这个模版函数能方便的释放COM接口并将它们的值设为NULL
safe_delete——这个模版函数能方便的删除一个对象并将指向其的指针设为NULL
wnd_proc——应用程序主窗口的回调函数
1.5.2 实例框架
通过实例框架,我们形成了一种通用的方法去构造示例程序。每一个例程都含有三个函数的实现,当然这不包括回调函数和WinMain主函数。这三个函数用特定的代码实现特定的功能。这三个函数是:
bool setup()——在这个函数里,我们将准备一切该程序需要用到的东西,包括资源的分配,检查设备能力,设置应用程序的状态
void clearup()——这个函数将释放Setup()中分配的资源,如分配的内存。
bool display(float time_delta)——这个函数包含所有与我们绘图和显示有关的代码。参数timeDelta为每一帧的间隔时间,用来控制每秒的帧数。
这个示例程序将创建并初始化一个Direct3D应用程序,并用黑色填充屏幕。注意,我们使用了通用函数简化了初始化过程。
/*********************************************************************************
PURPOISE:
Demonstrates how to initialize Direct3D, how to use framework functions,
and how to clear the screen to black.
*********************************************************************************/
#include "D3DUtility.h"
IDirect3DDevice9* g_device = NULL;
bool setup()
{
// nothing to setup in this sample
return true;
}
void cleanup()
{
// nothing to cleanup in this sample
}
bool display(float timeDelta)
{
// Only use Device methods if we have a valid device.
if(g_device == NULL)
return false;
// Instruct the device to set each pixel on the back buffer black - D3DCLEAR_TARGET: 0x00000000 (black);
// and to set each pixel on the depth buffer to a value of 1.0 - D3DCLEAR_ZBUFFER: 1.0f.
g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
// swap the back and front buffers
g_device->Present(NULL, NULL, NULL, NULL);
return true;
}
LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
if(wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, PSTR cmd_line, int cmd_show)
{
if(! init_d3d(inst, 640, 480, true, D3DDEVTYPE_HAL, &g_device))
{
MessageBox(NULL, "init_d3d() - failed.", 0, MB_OK);
return 0;
}
if(! setup())
{
MessageBox(NULL, "Steup() - failed.", 0, MB_OK);
return 0;
}
enter_msg_loop(display);
cleanup();
g_device->Release();
return 0;
}
Display方法调用了IDirect3DDevice::Clear方法,分别用黑色和1.0填充后备表面和深度/模版缓冲。如果应用程序不停止的话,我们会一直执行这个操作。IDirect3DDevice::Clear声明如下:
HRESULT 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::Present方法进行后备表面的交换。
Windows 回调函数为一组事件集,即,我们可按ESC键让程序退出。
最后,WinMain按如下步骤运行:
1. 初始化主显示窗口和Direct3D
2. 调用setup进行程序的准备工作
3. 使用display函数作为参数进入消息循环
4. 清除应用程序最后释放IDirecte3DDevice9对象
注意:不要忘了在你的工程中加入d3d9.lib、d3dx9.lib、winmm.lib这三个库!