Direct3D初始化大概分為4個步驟:
1.獲取接口IDirect3D9的指針。(Direct3DCreate9函數調用)。
該接口用戶獲取系統中物理硬件設備的信息並創建接口IDirect3DDevice9,此接口是一個C++對象,代表顯示3D圖形的物理硬件設備。
2.檢查設備性能(D3DCAPS9結構體),判斷主顯卡是否支持某些特性,比如是否支持頂點運算。創建IDirect3DDevice9之前,必須確定主顯卡支持的特性。
3.初始化D3DPRESENT_PARAMETER結構的一個實例,通過設置該結構體的成員變量來指定即將創建的借口IDirect3DDevice9的特性。
4.利用已經初始化的D3DPRESENT_PARAMETER結構創建IDirect3DDevice9對象。
以下是DirectX 9.0 3D游戲開發編程基礎的基本程序框架(包含初始化的過程):
d3dUtility.h
InitD3D:該函數對應用程序主窗口進行了初始化(windows程序設計的基礎,RegisterClass,CreateWindow,ShowWindow,UpdateWindow);
然后執行Direct3D初始化過程,返回一個指向已經創建好的IDirect3DDevice9接口的指針。
EnterMsgLoop:封裝了消息循環,參數為一個指向顯示函數的函數指針。空閑處理期間顯示場景。
Release:模板函數是方便地釋放COM接口並將其設置為NULL。
Delete:模板函數是方便地刪除自由堆對象,將其指針設置為NULL。
WndProc:主窗口的窗口過程函數,針對不同類型的消息進行相對應的相應。
#ifndef D3DUTILITY_H
#define D3DUTILITY_H
#include <d3dx9.h>
#include <windows.h>
#include <tchar.h>
namespace d3d {
bool InitD3D( HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9 **device);
//[in] 應用實例 [in] 后台緩存的寬 [in]后台緩存的高 [in] 是否窗口或者全屏 [in] 設備類型 HAL或REF [out] 初始化成功的IDirect3DDevice指針
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;
}
}
}
#endif
d3dUtility.cpp
上述接口的具體實現部分。
#include "d3dUtility.h"
namespace d3d {
bool InitD3D( HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9 **device) {
HWND hwnd; //窗口句柄
WNDCLASS wndclass; //窗口類別,在CreateWindow之前需要RegisterClass , 針對WNDCLASS的參數可以了解一下API
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = (WNDPROC)d3d::WndProc; //WndProc
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = _T("Direct3D9App");
if( !RegisterClass(&wndclass) ) {
MessageBox(0, _T("This program requires windows NT!"), 0, 0);
return false;
}
hwnd = CreateWindow(_T("Direct3D9App")/*0*/,_T("Direct3D9App")/*0*/,WS_EX_TOPMOST/*WS_OVERLAPPEDWINDOW*/,0,0,width,height,0,0,hInstance,0);
::ShowWindow(hwnd,SW_SHOW/*0*/);
::UpdateWindow(hwnd);
//完成了主窗口初始化的過程,以下部分實現Direct3D的初始化過程,並且設置IDirect3DDevice9指針
IDirect3D9 *_d3d9;
_d3d9 = Direct3DCreate9(D3D_SDK_VERSION); //獲取IDirect3D9的指針,進行設備的迭代,檢測特性&獲取IDirect3DDevice9指針准備
if( !_d3d9 )
{
::MessageBox(0, _T("Direct3DCreate9() - FAILED"), 0, 0);
return false;
}
D3DCAPS9 caps; //檢測圖形設備的支持的特性
_d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
int vp = 0; //是否支持硬件頂點運算
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) {
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
else {
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
D3DPRESENT_PARAMETERS d3dpp; //D3DPRESENT_PARAMETERS設置,設置IDirect3DDevice的特性
::ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));
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 = windowed;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
HRESULT hr = _d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, vp, &d3dpp, device);
if( FAILED(hr) ) {
::MessageBox(0, _T("CreateDevice() - Failed"), 0, 0);
return false;
}
return true;
}
int EnterMsgLoop( bool (*ptr_display)(float timeDelta) ) {
MSG msg;
::ZeroMemory( &msg, sizeof(MSG) );
static float lastTime = (float)timeGetTime();
while(msg.message != WM_QUIT) {
if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else { //空閑的時候進行繪制
float currTime = (float)timeGetTime();
float timeDelta = (currTime - lastTime)*0.001f;
ptr_display(timeDelta);
lastTime = currTime;
}
}
return msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch( msg ) {
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_KEYDOWN:
if(wParam == VK_ESCAPE) //esc退出主窗口程序
::DestroyWindow(hwnd);
break;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
}
commonFunc.h
此書中的3個例程函數:
Setup:設置和初始化部分在此函數中進行,如分配資源,檢查設備性能,設置應用程序狀態。
Cleanup:釋放Setup函數中分配的任何資源,如分配的存儲單元。
Display:實現全部的繪制代碼以及相鄰幀之間應該執行的操作,如更新物體的位置。參數timeDelta為相鄰幀的時間差,主要用於將動畫與顯示器的刷新頻率保持同步。
后續的章節的實現部分集中在這3個函數中,其他部分的代碼設計到Direct3D的初始化和Windows程序設計的基本概念,在其他章節中改動很少。
#ifndef COMMONFUNC_H
#define COMMONFUNC_H
#include "d3dUtility.h"
IDirect3DDevice9 *Device = 0;
bool windowed = true;
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->Present(0, 0, 0, 0);
}
return true;
}
#endif
Entry.cpp
window程序設計的入口,WinMain函數部分。
#include "d3dUtility.h"
#include "commonFunc.h"
int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd ) {
if( !d3d::InitD3D(hInstance, 800, 600, true, D3DDEVTYPE_HAL, &Device )) {
::MessageBox(0,_T("InitD3D() - Failed"), 0, 0 );
return 0;
}
if( !Setup() ) {
::MessageBox(0,_T("Setup() - Failed"), 0, 0 );
return 0;
}
d3d::EnterMsgLoop( Display );
Cleanup();
Device->Release();
return 0;
}
程序運行效果截圖:

