前段时间一直混迹于C++与C#语言,徘徊于DirectX与Xna之间,一直没什么大的收获。
重新拾回C++&DirectX,有一种返璞归真的感慨~ 多少有一些心得,简单总结一点~ 请园子的前辈们多多指教,多多拍砖^ ^
用过Xna的朋友都知道,客户在Xna中从来不用自己去实现3D设备的初始化,游戏的主循环,甚至是退出时的设备资源释放等等相关事宜。
不过很不幸,DirectX没有赐予大家这种特权,相关工作我们需要自己实现:
1.新建一个Win32工程,并在此基础上新增CD3DInit类(这里其实没有必要真正形成类,个人感觉全局函数用起来反倒方便);
/*
----------------------------------
代码清单:D3DInit.h
来自:
http://kenkao.cnblogs.com/
----------------------------------
*/
#pragma
once
#include
<
windows.h
>
#include
"
d3d9.h
"
#include
"
d3dx9.h
"
#pragma
comment(lib, "d3d9.lib")
#pragma
comment(lib, "d3dx9.lib")
#define
ReleaseCOM(x){if(x!=NULL) x->Release();x=NULL;}
HRESULT InitD3D(IDirect3D9
**
ppD3D,
IDirect3DDevice9
**
ppD3DDevice,
HWND hWnd, BOOL ForceWindowed
=
FALSE,
BOOL MultiThreaded
=
FALSE);
D3DInit.cpp
/*
----------------------------------
代码清单:D3DInit.cpp
来自:
http://kenkao.cnblogs.com/
----------------------------------
*/
#include
"
D3DInit.h
"
/*
---------------------------------------------------
参考了《Advanced Animation with DirectX》书中的实现方法
参数一:IDirect3D9指针引用(指针的指针)
参数二:IDirect3DDevice9设备指针引用
参数三:有效的窗口句柄
参数四:是否全屏
参数五:是否多线程
----------------------------------------------------
*/
HRESULT InitD3D(IDirect3D9
**
ppD3D,
IDirect3DDevice9
**
ppD3DDevice,
HWND hWnd,
BOOL ForceWindowed,
BOOL MultiThreaded)
{
IDirect3D9
*
pD3D
=
NULL;
IDirect3DDevice9
*
pD3DDevice
=
NULL;
HRESULT hr;
//
错误检查
if
(
!
ppD3D
||
!
ppD3DDevice
||
!
hWnd)
return
E_FAIL;
//
初始化 Direct3D
if
((pD3D
=
Direct3DCreate9(D3D_SDK_VERSION))
==
NULL)
return
E_FAIL;
*
ppD3D
=
pD3D;
//
是否使用全屏状态
int
Mode;
if
(ForceWindowed
==
TRUE)
Mode
=
IDNO;
else
Mode
=
MessageBox(hWnd,
"
Use fullscreen mode? (640x480x16)
"
,
"
Initialize D3D
"
, MB_YESNO
|
MB_ICONQUESTION);
//
全屏与非全屏状态下的参数设置
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(
&
d3dpp,
sizeof
(d3dpp));
if
(Mode
==
IDYES) {
DWORD Width
=
640
;
DWORD Height
=
480
;
D3DFORMAT Format
=
D3DFMT_R5G6B5;
d3dpp.BackBufferWidth
=
Width;
d3dpp.BackBufferHeight
=
Height;
d3dpp.BackBufferFormat
=
Format;
d3dpp.SwapEffect
=
D3DSWAPEFFECT_FLIP;
d3dpp.Windowed
=
FALSE;
d3dpp.EnableAutoDepthStencil
=
TRUE;
d3dpp.AutoDepthStencilFormat
=
D3DFMT_D16;
d3dpp.FullScreen_RefreshRateInHz
=
D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval
=
D3DPRESENT_INTERVAL_DEFAULT;
}
else
{
RECT ClientRect, WndRect;
GetClientRect(hWnd,
&
ClientRect);
GetWindowRect(hWnd,
&
WndRect);
DWORD DesiredWidth
=
640
;
DWORD DesiredHeight
=
480
;
DWORD Width
=
(WndRect.right
-
WndRect.left)
+
(DesiredWidth
-
ClientRect.right);
DWORD Height
=
(WndRect.bottom
-
WndRect.top)
+
(DesiredHeight
-
ClientRect.bottom);
MoveWindow(hWnd, WndRect.left, WndRect.top, Width, Height, TRUE);
D3DDISPLAYMODE d3ddm;
pD3D
->
GetAdapterDisplayMode(D3DADAPTER_DEFAULT,
&
d3ddm);
d3dpp.BackBufferWidth
=
DesiredWidth;
d3dpp.BackBufferHeight
=
DesiredHeight;
d3dpp.BackBufferFormat
=
d3ddm.Format;
d3dpp.SwapEffect
=
D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed
=
TRUE;
d3dpp.EnableAutoDepthStencil
=
TRUE;
d3dpp.AutoDepthStencilFormat
=
D3DFMT_D16;
d3dpp.FullScreen_RefreshRateInHz
=
D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval
=
D3DPRESENT_INTERVAL_DEFAULT;
}
//
创建3D设备
DWORD Flags
=
D3DCREATE_MIXED_VERTEXPROCESSING;
if
(MultiThreaded
==
TRUE)
Flags
|=
D3DCREATE_MULTITHREADED;
if
(FAILED(hr
=
pD3D
->
CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, hWnd, Flags,
&
d3dpp,
&
pD3DDevice)))
return
hr;
*
ppD3DDevice
=
pD3DDevice;
//
设置投影矩阵
float
Aspect
=
(
float
)d3dpp.BackBufferWidth
/
(
float
)d3dpp.BackBufferHeight;
D3DXMATRIX matProjection;
D3DXMatrixPerspectiveFovLH(
&
matProjection, D3DX_PI
/
4.0f
, Aspect,
1.0f
,
10000.0f
);
pD3DDevice
->
SetTransform(D3DTS_PROJECTION,
&
matProjection);
//
设置默认渲染状态
pD3DDevice
->
SetRenderState(D3DRS_LIGHTING, FALSE);
pD3DDevice
->
SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
pD3DDevice
->
SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
pD3DDevice
->
SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
//
设置默认纹理融合态
pD3DDevice
->
SetTextureStageState(
0
, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pD3DDevice
->
SetTextureStageState(
0
, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pD3DDevice
->
SetTextureStageState(
0
, D3DTSS_COLOROP, D3DTOP_MODULATE);
//
设置默认纹理采样器
pD3DDevice
->
SetSamplerState(
0
, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pD3DDevice
->
SetSamplerState(
0
, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
return
S_OK;
}
D3DInit.h相当于一个总的头文件,我们可以把用于初始化D3D所需要引入的头文件、库文件统统放到这个下面,供其他文件引用。
D3DInit.cpp是其实现部分,目前只有一个用于快速初始化D3D设备的InitD3D函数,代码中给出了参考书名,不得不佩服与作者的匠心独运。函数内部给出了中文注释,其奥妙留待读者自己体会~
至于ReleaseCOM宏,可以作为通用的COM安全释放函数,熟练运用D3D的朋友大都会留出这样一个宏,建议大家养成这样一个习惯~
2.新增CD3DGame类
/*
----------------------------------
代码清单:D3DGame.h
来自:
http://kenkao.cnblogs.com/
----------------------------------
*/
#pragma
once
#include
"
D3DInit.h
"
//
初始化
void
Initialize(HWND hWnd);
//
内容加载
void
LoadContent();
//
逻辑更新
void
Update();
//
绘图更新
void
Draw();
//
卸载内容
void
UnloadContent();
//
释放并退出
void
Dispose();
/*
---------------------------------------------------------
1.用如下这一段替代_tWinMain消息循环的那一段
LoadContent();
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Update();
Draw();
}
UnloadContent();
Dispose();
2.InitInstance的ShowWindow(hWnd, nCmdShow)之前加入
Initialize(hWnd);
----------------------------------------------------------
*/
/*
----------------------------------
代码清单:D3DGame.cpp
来自:
http://kenkao.cnblogs.com/
----------------------------------
*/
#include
"
StdAfx.h
"
#include
"
D3DGame.h
"
IDirect3D9
*
g_pD3D
=
NULL;
IDirect3DDevice9
*
g_pD3DDevice
=
NULL;
void
Initialize(HWND hWnd)
{
InitD3D(
&
g_pD3D,
&
g_pD3DDevice, hWnd);
}
void
LoadContent()
{
}
void
Update()
{
}
void
Draw()
{
g_pD3DDevice
->
Clear(
0
, NULL, D3DCLEAR_TARGET
|
D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(
100
,
149
,
237
,
255
),
1.0f
,
0
);
if
(SUCCEEDED(g_pD3DDevice
->
BeginScene()))
{
g_pD3DDevice
->
EndScene();
}
g_pD3DDevice
->
Present(NULL, NULL, NULL, NULL);
}
void
UnloadContent()
{
}
void
Dispose()
{
ReleaseCOM(g_pD3DDevice);
ReleaseCOM(g_pD3D);
}
安插这两个文件的目的,主要是为了构建我们自己的D3D游戏框架,各个函数的用途均已说明,完全仿Xna结构而来~(用习惯了^ ^)
我们可以按照D3DGame.h最下方的注释,将这个框架跟Win32默认代码框架关联起来。
以后所有的编码工作均在我们自己的这部分代码中实现,基本不需要再关心令人眼花缭乱的Win32工程代码。
3.改动后的Win32代码为:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
MSG msg;
HACCEL hAccelTable;
// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_KEN3DGAME, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_KEN3DGAME));
LoadContent();
ZeroMemory(&msg, sizeof(MSG));
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Update();
Draw();
}
UnloadContent();
Dispose();
return (int) msg.wParam;
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // 将实例句柄存储在全局变量中
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
Initialize(hWnd);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
这样,一个可以无限复用的D3D基本框架就完成了~
如下执行效果图: