1.Direct3D概述
Direct3D是一套底层图形API,借助该API,我们能够利用硬件加速功能来绘制3D场景。
设备制造商将其产品支持的全部功能都实现到HAL中。
1.1 Direct3D提供了参考光栅设备(REF设备),它以软件运算方式完全支持Direct3D API。REF速度缓慢,在测试以外的其他环节都不适用。
1.2 D3DDEVTYPE枚举类型
在程序中HAL设备用值D3DDEVTYPE_HAL来表示,REF设备用值D3DDEVTYPE_REF来表示。
2.COM:创建COM接口时不可以使用new关键字,使用完用Realease方法释放。COM接口都有一个前缀I。
3.预备知识
3.1.表面是Direct3D主要用于存储2D图像数据的一个像素矩阵,实际上像素数据存储在一个线性数组中。
在代码中我们用接口IDirect3DSurface9来描述表面,接口IDirect3DSurface9最重要的方法如下:LockRect()[该方法获取指向表面存储区的指针],UnlockRect(),GetDesc()[该方法通过填充结构D3DSURFACE_DESC来获取表面的描述信息]。
3.2多重采样是一种用于平滑块状图像的技术。
D3DMULTISAMPLE_TYPE枚举类型包含一系列枚举常量,用于对表面进行多重采样的级别。
使用IDirect3D9:: CheckDeviceMultiSampleType()方法检查自己的图像设备是否支持所希望的多重采样类型。
3.3像素格式 创建表面或纹理时,可以用D3DFORMAT枚举类型的枚举常量来定义。较常用的有:
D3DFMT_R8G8B8,每个像素有24位组成,8位分给红色,8位给绿色,8位给蓝色。
D3DFMT_X8R8G8B8,每个像素有32位组成,8位未用,8位红色,8位给绿色,8位给蓝色。
D3DFMT_A8R8G8B8,每个像素有32位组成,8位Alpha值,8位红色,8位绿色,8位给蓝色。
3.4内存池:表面和其他的Direct3D资源可以放在内存池中,内存池的类型可以用D3DPOOL枚举类型来定义。
D3DPOOL_DEFAULT,D3DPOOL_MANAGED,D3DPOOL_SYSTEMMEM,D3DPOOL_SCRATCH ,D3DPOOL_FORCE_DWORD
3.5交换链和页面置换
交换链:该表面集合用接口IDirect3DSwapChain9来表示。目的是让页面显示的更加流畅。
3.6深度缓存(Z-buffering)是一个只包含页面的深度信息,不包含图像数据的表面。深度缓存为每一个像素都保存了深度项。D3DFORMAT枚举其值。常用的如下:
D3DFMT_D32 指定32位的深度缓存。
D3DFMT_D24S8 32位Z-buffering,24位深度缓存,8位供模板缓存使用。
D3DFMT_D24X8 32位Z-buffering,24位深度缓存,8位未用。
D3DFMT_D24X4S4 32位Z-buffering,24位深度缓存,4位未用,4位供模板缓存使用。
3.7顶点运算 顶点是3D几何学的基本元素,在Direct3D中我们可以使用软件顶点运算或者硬件顶点运算。始终优先考虑硬件顶点运算。
3.8设备性能 Direct3D所提供的每一项性能都对应于结构D3DCAPS9中的一个数据成员或某一位。以某一硬件为基础,初始化一个D3DCAPS9类型的实例,然后和其实例相应的数据位进行比对。 D3DCAPS9::DevCaps表示设备性能。
4.Direct3D的初始化分为以下步骤:
4.1获取IDirect3D9的指针。
IDirect3D9* d3d9 = 0;
if( NULL == (d3d9 = Direct3DCreate9(D3D_SDK_VERSION)))
{ return E_FAIL;}
4.2校验硬件顶点运算。
D3DCAPS9 caps;
d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps);//初始化主显卡的性能参数。
int vp = 0;
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )//对设备性能进行检查
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
4.3填充D3DPRESENT_PARAMETERS结构
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 = windowed;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
4.4创建IDirect3DDevice9接口,用CreateDevice()函数。
hr = d3d9->CreateDevice(
D3DADAPTER_DEFAULT, // primary adapter
deviceType, // device type
hwnd, // window associated with device
vp, // vertex processing
&d3dpp, // present parameters
device); // return created device
if( FAILED(hr) )
{
d3d9->Release(); // done with d3d9 object
::MessageBox(0, "CreateDevice() - FAILED", 0, 0);
return false;
}
创建每个程序都需要完成一些共性任务,如创建窗口,初始化Direct3D,进入消息循环等。这些我们将其封装在了d3dUtility.h和d3dUtility.cpp文件中。如下为具体代码,项目需加入库文件d3dx9.lib;d3d9.lib;winmm.lib;
#ifndef __d3dUtilityH__ #define __d3dUtilityH__ #include <d3dx9.h> #include <string> namespace d3d { bool InitD3D( HINSTANCE hInstance, // [in] Application instance. int width, int height, // [in] Backbuffer 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; } } } #endif // __d3dUtilityH__
#include "d3dUtility.h" bool d3d::InitD3D( HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9** device) { // // Create the main application window. // WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)d3d::WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = "Direct3D9App"; if( !RegisterClass(&wc) ) { ::MessageBox(0, "RegisterClass() - FAILED", 0, 0); return false; } HWND hwnd = 0; hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App", WS_EX_TOPMOST, 0, 0, width, height, 0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/); if( !hwnd ) { ::MessageBox(0, "CreateWindow() - FAILED", 0, 0); return false; } ::ShowWindow(hwnd, SW_SHOW); ::UpdateWindow(hwnd); // // Init D3D: // HRESULT hr = 0; // Step 1: Create the IDirect3D9 object. IDirect3D9* d3d9 = 0; d3d9 = Direct3DCreate9(D3D_SDK_VERSION); if( !d3d9 ) { ::MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0); return false; } // Step 2: Check for hardware vp. D3DCAPS9 caps; d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &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 = windowed; 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. hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, // primary adapter deviceType, // device type hwnd, // window associated with device vp, // vertex processing &d3dpp, // present parameters device); // return created device if( FAILED(hr) ) { // try again using a 16-bit depth buffer d3dpp.AutoDepthStencilFormat = D3DFMT_D16; hr = d3d9->CreateDevice( D3DADAPTER_DEFAULT, deviceType, hwnd, vp, &d3dpp, device); if( FAILED(hr) ) { d3d9->Release(); // done with d3d9 object ::MessageBox(0, "CreateDevice() - FAILED", 0, 0); return false; } } d3d9->Release(); // done with d3d9 object return true; } 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(::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; }
#include "d3dUtility.h" IDirect3DDevice9* Device = 0; // // Framework Functions // bool Setup() { // Nothing to setup in this sample. return true; } void Cleanup() { // Nothing to cleanup in this sample. } bool Display(float timeDelta) { if( Device ) // Only use Device methods if we have a valid device. { Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0); Device->Present(0, 0, 0, 0); } return true; } // // WndProc // LRESULT CALLBACK d3d::WndProc(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); } // // WinMain // int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, 640, 480, 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; }