DirectX(Direct eXtension, DX)是由Microsoft创建的多媒体编程接口,它能够让多媒体软件程序在基于Windows的具有DirectX兼容硬件与驱动程序的计算机上运行,同时可保证硬件的高性能使用。DirectX包括两个方面:一是DirectX SDK,用于开发DirectX应用程序;二是DirectX Runtime,是一组动态链接库,用于开发和运行DirectX应用程序。而Direct3D主要负责3D效果的显示,下面主要介绍Direct3D的编程方法。
一、基本概念
1.1 Direct3D接口对象
Direct3D是基于组件对象模型COM的设计结构,因此应用程序需用通过COM接口对象的接口的指针执行COM接口对象的功能。
同时COM接口对象与C++类对象的区别是,C++类对象由new和delete控制对象的生存周期,而COM接口对象则通过控制该对象的引用计数来决定其生存周期,即一般调用Release来释放引用结束的接口。
编制基于Direct3D的程序,首先需要两个接口对象的指针,一是IDirect3D9接口对象的指针,可通过Direct3DCreate9函数获得;二是IDirect3DDevice9接口对象的指针,可利用IDirect3D9接口对象指针的CreateDevice函数获得。一般这两个指针定义为全局变量,可用于程序的任何地方。下面关于Direct3D的编程主要依靠于上述的IDirect3DDevice9接口对象指针。
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
1.2 顶点与图元
Direct3D绘制图形的基础是预设的顶点坐标,在Direct3D中,可以创建自定义灵活顶点格式FVF,通过用一个简单宏来说明自定义的FVF格式,Direct3D就能够正确获取顶点的信息,如坐标、颜色等。具体编程是通过顶点缓存的创建、访问、关联、绘制等步骤实现,一般可用到CreateVertexBuffer、Lock、Unlock、SetStreamSource、DrawPrimitive等函数。
g_pd3dDevice->CreateVertexBuffer(8*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVertexBuf, NULL);
CUSTOMVERTEX *pVertices = NULL;
g_pVertexBuf->Lock(0, 0,(void**)&pVertices, 0);
pVertices[0] = CUSTOMVERTEX(-5.0F, 5.0F, -5.0F, D3DCOLOR_XRGB(255, 0, 0));
/* Set the Vertex Value */
pVertices[7] = CUSTOMVERTEX( 5.0F, -5.0F, -5.0F, D3DCOLOR_XRGB( 0,255, 0));
g_pVertexBuf->Unlock();
对于复杂的对象(这种复杂很常见),更多地使用索引缓存的方法,索引缓存只记录要绘制顶点在顶点缓存中的索引值,这种方式避免了顶点数据的重复存储,缩小了程序的数据存储空间。具体编程需要在顶点缓存创建、访问的基础上,同样也需要创建、访问、关联、绘制索引缓存,相关的函数主要有CreateIndexBuffer、Lock、Unlock、SetIndices、DrawIndexedPrimitive等。
g_pd3dDevice->CreateIndexBuffer(36*sizeof(WORD), 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIndexBuf, NULL);
WORD *pIndices = NULL;
g_pIndexBuf->Lock(0, 0, (void**)&pIndices, 0);
pIndices[0] = 0; pIndices[1] = 1; pIndices[2] = 2;
/* Set the Indices Value */
pIndices[33] = 4; pIndices[34] = 7; pIndices[35] = 6;
g_pIndexBuf->Unlock();
1.3 渲染状态
Direct3D提供了多种渲染状态来决定几何物体如何被渲染,它可用于控制Direct3D设备光栅化模型的行为,包括绘制物体的着色模式、雾化操作及其他光栅化操作等。
渲染状态可通过SetRenderState函数来实现,比较重要的状态有D3DRS_CULLMODE、D3DRS_SHADEMODE、D3DRS_FILLMODE等。
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
二、程序框架
DXUT(DirectX Utility Library)是建立在Direct3D 9和Direct3D 10上的一个程序框架,它简化了Direct3D应用程序的开发。这里暂时并不使用该框架,仅采用Windows应用程序的创建方法来搭建Direct3D程序框架。
程序中关于Direct3D的部分主要包括Direct3D程序初始化、绘制3D场景、结束Direct3D程序等。具体介绍如下:
(1) Direct3D程序初始化,包括创建Direct3D接口、获取硬件设备信息、创建Direct3D设备接口、Direct3D接口的释放等;
(2) 绘制3D场景,包括清空背景、图形绘制准备、图形绘制、翻转等;
(3) 结束Direct3D程序,用来释放创建的对象,包括设备接口对象、顶点缓存、索引缓存等。
当然程序需要注意一些其他的东西,如头文件的包含、库文件的链接等,具体包括:<d3d9.h> <d3dx9.h> <dxerr.lib> <dxguid.lib> <d3d9.lib> <d3dx9.lib> <winmm.lib>等。
#include <d3d9.h> /* 头文件包含 */
/* 链接库文件 d3d9.lib 和 d3dx9.lib */
/* 全局变量 */
LPDIRECT3D9 g_pD3D = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // Our rendering device
/* 初始化Direct3D */
HRESULT InitD3D( HWND hWnd )
{
// Create the D3D object, which is needed to create the D3DDevice.
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Set up the structure used to create the D3DDevice. Most parameters are
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
// Create the Direct3D device.
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
return E_FAIL;
}
// Other Adding codes
return S_OK;
}
/* 释放所有对象 */
VOID Cleanup()
{
if( g_pd3dDevice != NULL)
g_pd3dDevice->Release();
if( g_pD3D != NULL)
g_pD3D->Release();
}
/* 渲染3D场景 */
VOID Render()
{
if( NULL == g_pd3dDevice )
return;
// Clear the backbuffer to a blue color
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// Begin the scene
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// Rendering of scene objects can happen here
// End the scene
g_pd3dDevice->EndScene();
}
// Present the backbuffer contents to the display
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
/* 消息处理主循环 */
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
/* 主函数部分 */
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
L"D3D Tutorial", NULL };
RegisterClassEx( &wc );
// Create the application's window
HWND hWnd = CreateWindow( L"D3D Tutorial", L"D3D Tutorial",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance, NULL );
// Initialize Direct3D
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
// Show the window
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );
MSG mssg;
PeekMessage( &mssg, NULL, 0, 0, PM_NOREMOVE);
// run till completed
while (mssg.message!=WM_QUIT)
{
// is there a message to process?
if (PeekMessage( &mssg, NULL, 0, 0, PM_REMOVE))
{
// dispatch the message
TranslateMessage(&mssg);
DispatchMessage(&mssg);
}
else
{
//No message to process
Render();
}
}
}
UnregisterClass( L"D3D Tutorial", wc.hInstance );
return 0;
}
本文为个人读书笔记,主要着重于编程学习,参考文献包括:
[1] 郑阿奇. DirectX 3D游戏编程实用教程[M]. 北京:电子工业出版社, 2011.
[2] Introduction to 3D Game Programming with DirectX 9.