要开始D3D编程,首先是要初始化D3D环境,SDK文档的tutorial将整个过程分为几部
初始化D3D
关闭D3D,退出程序
创建窗体
之前说过,D3D程序是在一个windows窗体的客户区进行绘制,D3D相当于画笔的话,这个窗体就是画布,所以在初始化D3D之前首先要有一个窗体,根据简单的Windows API编程回顾的内容,创建一个简单窗体并不难。
定义窗口类
WNDCLASS wclass;
注册窗口类
RegisterClass(&wclass);
创建窗口
CreateWindow(...);
初始化D3D
一旦创建好了窗体,就可以在这个基础上进一步去初始化D3D,创建D3D设备,以在窗体这个画布上进行绘图,初始化D3D的过程可以分为四部。
获取IDirect3D9的指针,游戏开发中最常用的就是COM配合DirectX技术,这儿IDirect3D9就是一个表示D3D9的COM接口,因此不能直接使用C++的new操作符
LPDIRECT3D9 g_pD3D9; //IDirect3D9 *g_pD3D9;
g_pD3D9 = Direct3DCreate9(D3D_SDK_VERSION);
检查设备性能,对API的支持程度(如硬件TnL,这个是比较老的固定功能流水线中所采用的一些东西,现代的可编程流水线已经见不着了)
//以硬件TnL为例
D3DCAPS9 d3dcap;
int hwTnL = 0;
g_pD3D9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dcaps);
if(caps.Devcaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
hwTnL = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
hwTnL = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
建立一个D3DPRESENT_PARAMETERS
D3DPRESENT_PARAMETERS d3dpp;
//按需设置d3dpp的各类属性
建立D3D设备
g_pD3D9->CreateDevice(D3DADAPTER,
D3DDEVTYPE,
HWND, BEHAVIOR_FLAGS
D3DPRESENT_PARAMETERS*,
IDirect3DDevice9 **);
至此,D3D的初始化就完成了
处理消息
就如简单的Windows API编程回顾中所述,游戏程序多数不是事件驱动的,因而可以使用PeekMessage处理消息
MSG msg;
while(PeekMessage(&msg, 0, 0, 0, 0))
{
if(msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//Game Codes
渲染&显示场景
清除后台缓存,将后台缓存置为所设定的颜色,将深度或模板缓存设置为需要的值
//IDirect3DDevice9::Clear
g_pD3DDEV9->Clear(DWORD Count, const D3DRECT *pRects,
DWORD flags, D3DCOLOR Color, float Z, DWORD Stencil);
向后台缓存绘制场景
g_pD3DDEV9->BeginScene();
//Render Codes
g_pD3DDEV9->EndScene();
提交下一帧后台缓存,仅仅是提交,还没有显示
//IDirect3DDevice9::Present
g_pD3DDEV9->Present(0,0,0,0);
关闭D3D,退出程序
退出程序不困难,但之前要先关闭D3D。之前说了g_pD3D9和g_pD3DDEV9是两个COM接口指针,而COM不可以使用C++的new/delete操作符来创建和销毁,所以需要手动的来释放一下
g_pD3D9->Release();
g_pD3D9 = NULL;
g_pD3DDEV9->Release();
g_pD3DDEV9 = NULL;
全部代码如下
#include <windows.h>
#include <d3d9.h>
#include <strsafe.h>
#pragma comment(lib, "d3d9.lib")
LPDIRECT3D9 g_pD3D9;
LPDIRECT3DDEVICE9 g_pD3D9dev;
HWND hMain;
bool initWinApp(HINSTANCE hInstance, int iCmdShow);
LRESULT CALLBACK MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT D3DInit(HWND hWnd);
void D3DRender();
void D3DCleanup();
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmd, int iCmdShow)
{
if( !initWinApp(hInstance, iCmdShow) )
{
::MessageBox(0, TEXT("initWinApp Failed!"), TEXT("ERROR"), MB_OK);
return 0;
}
if( SUCCEEDED(D3DInit(hMain)) )
{
::ShowWindow(hMain, iCmdShow);
::UpdateWindow(hMain);
MSG msg;
while( PeekMessage(&msg, 0, 0, 0, 0 ) )
{
if( msg.message == WM_QUIT )
break;
msg.message = WM_PAINT;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
D3DCleanup();
UnregisterClass(L"D3DInit", hInstance);
return 0;
}
bool initWinApp(HINSTANCE hInstance, int iCmdshow)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MsgProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = ::LoadIcon(0, IDI_APPLICATION);
wc.hCursor = ::LoadCursor(0, IDC_ARROW);
wc.hbrBackground= static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH));
wc.lpszMenuName = 0;
wc.lpszClassName= TEXT("D3DInit");
if( !RegisterClass(&wc) )
{
::MessageBox(0, TEXT("RegisterClass Failed!"), TEXT("ERROR"), MB_OK);
return false;
}
if( (hMain = CreateWindow(TEXT("D3DInit"), TEXT("D3DInit"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL)) == NULL)
{
::MessageBox(0, TEXT("CreateWindow Failed!"), TEXT("ERROR"), MB_OK);
return false;
}
return true;
}
HRESULT D3DInit(HWND hWnd)
{
//acquire pointer to IDirect3D9
if( (g_pD3D9 = Direct3DCreate9(D3D_SDK_VERSION)) == NULL )
{
::MessageBox(0, TEXT("Direct3DCreate Failed!"), TEXT("ERROR"), MB_OK);
return E_FAIL;
}
//Check Device Capabilities, here we check the hardware TnL support
D3DCAPS9 caps;
g_pD3D9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
int hardwareTnL = 0;
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
hardwareTnL = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
hardwareTnL = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
//setup D3DPRESENT_PARAMETERS which defines the behavior of the D3D app
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = 800;
d3dpp.BackBufferHeight = 600;
d3dpp.BackBufferCount = 2;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hMain;
d3dpp.Windowed = 1;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz= D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
//Create D3D device
if( FAILED(g_pD3D9->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
hardwareTnL,
&d3dpp,
&g_pD3D9dev)) )
{
MessageBox(0, TEXT("CreateDevice Failed"), TEXT("ERROR"), MB_OK);
return E_FAIL;
}
return S_OK;
}
LRESULT CALLBACK MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
case WM_PAINT:
D3DRender();
ValidateRect(hWnd, NULL);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
void D3DRender()
{
//Set BackBuffer
g_pD3D9dev->Clear(0, NULL, D3DCLEAR_TARGET,D3DCOLOR_XRGB( 0, 255, 0 ), 1.0f, 0 );
//Render in this section
if( SUCCEEDED(g_pD3D9dev->BeginScene()) )
{
g_pD3D9dev->EndScene();
}
//Present BackBuffer
g_pD3D9dev->Present(NULL, NULL, NULL, NULL);
}
void D3DCleanup()
{
if( g_pD3D9dev != NULL )
{
g_pD3D9dev->Release();
}
if( g_pD3D9 != NULL )
{
g_pD3D9->Release();
}
}