Direct3D初始化(下)
Direct3D初始化步骤:
(1)获取接口 IDirect3D9 的指针。
(2)检查设备性能(D3DCAPS9),判断主显卡是否支持硬件顶点运算。
(3)初始化 D3DPRESENT_PARAMETERS 结构体的一个实例。该结构体的众多变量可以指定将要创建的接口 IDirect3DDevice9 的特性。
(4)利用已经初始化的 D3DPRESENT_PARAMETERS 结构体创建IDirect3DDevice9对象(一个C++对象,代表显示3D图像的物理硬件设备)。
第一步:获取接口 IDirect3D9 的指针
IDirect3D9 *d3d9;
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
第二步:检验硬件定点运算
我们需要检查主显卡是否支顶点运算。
所用函数:
HRESULT IDirect3D9::GetDeviceCaps(
UINT Adapter, //指定物理显卡序号
D3DDEVTYPE DeviceType, //指定设备类型(D3DDEVTYPE_HAL 硬件设备, D3DDEVTYPE_REF 软件设备)
D3DCAPS9 *pCaps //返回设备性能结构体实例
);
具体代码如下:
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; //软件顶点运算类型
}
//注意,我们用变量vp保存顶点运算类型, 因为在创建IDirect3DDevice类型
//的对象的时候,必须指定以后所需要使用的顶点运算类型。
第三步:填充D3DPRESENT_PARAMETERS结构体
typedef struct D3DPRESENT_PARAMETERS {
UINT BackBufferWidth; //后台缓存中表面的宽度
UINT BackBufferHeight; //后台缓存中表面的高度
D3DFORMAT BackBufferFormat; //后台缓存的像素格式 (如32位像素格式:D3DFMT_A8R8G8B8)
UINT BackBufferCount; //后台缓存的个数
D3DMULTISAMPLE_TYPE MultiSampleType; //后台缓存采用的多重采样类型
DWORD MultiSampleQuality; //多重采样的质量水平
D3DSWAPEFFECT SwapEffect; //交换链缓存的页面置换方式
HWND hDeviceWindow; //窗口句柄
BOOL Windowed; //true为窗口模式,false为全屏模式
BOOL EnableAutoDepthStencil; //为true,则自动创建并维护深度缓存或模板缓存
D3DFORMAT AutoDepthStencilFormat; //深度缓存或者模板缓存的像素格式
DWORD Flags; //一些附加的特征。
UINT FullScreen_RefreshRateInHz; //刷新频率
UINT PresentationInterval; //D3DPRESENT集合的一个成员
} D3DPRESENT_PARAMETERS, *LPD3DPRESENT_PARAMETERS;
详细参数说明,见后续文章【DirectX 9.0学习之路(第三话)】——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;
第四步:创建IDirect3DDevice9接口
使用的方法:
HRESULT CreateDevice(
[in] UINT Adapter, //指定IDirect3DDevice9对象代表那块物理显卡
[in] D3DDEVTYPE DeviceType, //指定使用的设备类型
[in] HWND hFocusWindow, //窗口句柄
[in] DWORD BehaviorFlags,
[in, out] D3DPRESENT_PARAMETERS *pPresentationParameters, //返回已经完成初始化的D3DPRESENT_PARAMETERS 类型的实例指针(很重要)
[out, retval] IDirect3DDevice9 **ppReturnedDeviceInterface //返回所创建的设备
);
下面是一个调用实例:
D3DPRESENT_PARAMETERS d3dpp;
IDirect3DDevice9 *device = 0;
hr = d3d9->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp,
&device);
if(FAILED(hr)) {
::MessageBox(0, "CreateDevice() - FAILED", 0, 0);
return 0;
}
在开始写代码之前,首先我们将Direct3D的初始化相关的操作封装于d3dUtility.h/d3dUtility.cpp文件中。在以后的工程中可以直接调用,提高开发效率。
d3dUtility.h头文件:
//d3dUtility.h文件
#include <d3dx9.h>
namespace d3d {
//D3D初始化函数声明
bool InitD3D(
HINSTANCE hInstance,
int width,
int height,
bool windowed,
D3DDEVTYPE deviceType,
IDirect3DDevice9 **device);
//消息循环声明
int EnterMsgLoop(
bool (*ptr_display)(float timeDelta)
);
//消息处理函数声明
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
//释放COM对象
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;
}
}
}
d3dUtility.cpp源文件
//d3dUtility.cpp源文件
bool d3d::InitD3D(
HINSTANCE hInstance,
int width,
int height,
bool windowed,
D3DDEVTYPE deviceType,
IDirect3DDevice9 **device)
{
//第一步:获取IDirect3D9对象指针
IDirect3D9 *d3d9;
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
//第二步:检验硬件顶点运算
D3DCAPS9 caps;
d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps);
int vp = 0;
if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
{
vp = D3DCREATE_HAEDWARE_VERTEXPROCESSING;
}
else {
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
//第三步:填充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;
//第四步:创建IDirect3DDevice9接口
IDirect3DDevice9 *device = 0;
HRESULT hr;
hr = d3d9->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_HAEDWARE_VERTEXPROCESSING,
&d3dpp,
&device);
if(FAILED(hr)) {
::MessageBox(0, "CreateDevice() - FAILED", 0, 0);
return 0;
}
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 currentTime = (float)timeGetTime();
float timeDelta = (currentTime - lastTime)*0.001f;
ptr_display(timeDelta); //渲染
lastTime = currentTime;
}
}
return msg.wParam;
}
程序框架:
(1) bool SetUp() 例程所需要进行的全部设置和初始化。
(2) void Display(float timeDelta) 实现全部的绘制代码以及相邻帧之间应执行的操作,例如更新物体的位置。参数timeDelta是相邻帧之间的时间差,主要用于将动画与显示器的刷新频率保持同步。
综合例程: