利用.x文件模型渲染三維模型,首先需要將.x文件中的各種數(shù)據(jù)分別加載到內(nèi)存中,主要包括頂點(diǎn)數(shù)據(jù)、材質(zhì)數(shù)據(jù)和紋理數(shù)據(jù)等。
網(wǎng)格模型接口ID3DXMESH
Direct3D擴(kuò)展實(shí)用庫(kù)定義了多邊形網(wǎng)格模型接口ID3DXMesh來(lái)表示一個(gè)復(fù)雜的三維物體模型,它是一個(gè)COM接口,繼承自ID3DXBaseMesh。
Direct3D擴(kuò)展實(shí)用庫(kù)函數(shù)D3DXCreateMesh()可用于創(chuàng)建一個(gè)Direct3D網(wǎng)格模型對(duì)象,該函數(shù)聲明如下:
Creates a mesh object using a declarator.
HRESULT D3DXCreateMesh(
DWORD NumFaces,
DWORD NumVertices,
DWORD Options,
CONST LPD3DVERTEXELEMENT9 * pDeclaration,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXMESH * ppMesh
);
Parameters
- NumFaces
- [in] Number of faces for the mesh. The valid range
for this number is greater than 0, and one less than the maximum DWORD
(typically 65534), because the last index is reserved.
- NumVertices
- [in] Number of vertices for the mesh. This
parameter must be greater than 0.
- Options
- [in] Combination of one or more flags from the
D3DXMESH enumeration, specifying options for the mesh.
- pDeclaration
- [in] Array of D3DVERTEXELEMENT9 elements,
describing the vertex format for the returned mesh. This parameter must map
directly to a flexible vertex format (FVF).
- pD3DDevice
- [in] Pointer to an IDirect3DDevice9 interface, the
device object to be associated with the mesh.
- ppMesh
- [out] Address of a pointer to an ID3DXMesh
interface, representing the created mesh object.
Return Values
If the function succeeds, the return value is D3D_OK.
If the function fails, the return value can be one of the following:
D3DERR_INVALIDCALL, E_OUTOFMEMORY.
D3DXMESH
Flags used to specify creation options for a mesh.
typedef enum D3DXMESH
{
D3DXMESH_32BIT = 0x001,
D3DXMESH_DONOTCLIP = 0x002,
D3DXMESH_POINTS = 0x004,
D3DXMESH_RTPATCHES = 0x008,
D3DXMESH_NPATCHES = 0x4000,
D3DXMESH_VB_SYSTEMMEM = 0x010,
D3DXMESH_VB_MANAGED = 0x020,
D3DXMESH_VB_WRITEONLY = 0x040,
D3DXMESH_VB_DYNAMIC = 0x080,
D3DXMESH_VB_SOFTWAREPROCESSING = 0x8000,
D3DXMESH_IB_SYSTEMMEM = 0x100,
D3DXMESH_IB_MANAGED = 0x200,
D3DXMESH_IB_WRITEONLY = 0x400,
D3DXMESH_IB_DYNAMIC = 0x800,
D3DXMESH_IB_SOFTWAREPROCESSING = 0x10000,
D3DXMESH_VB_SHARE = 0x1000,
D3DXMESH_USEHWONLY = 0x2000,
D3DXMESH_SYSTEMMEM = 0x110,
D3DXMESH_MANAGED = 0x220,
D3DXMESH_WRITEONLY = 0x440,
D3DXMESH_DYNAMIC = 0x880,
D3DXMESH_SOFTWAREPROCESSING = 0x18000,
} D3DXMESH, *LPD3DXMESH;
Constants
- D3DXMESH_32BIT
- The mesh has 32-bit indices instead of 16-bit
indices. See Remarks.
- D3DXMESH_DONOTCLIP
- Use the D3DUSAGE_DONOTCLIP usage flag for vertex
and index buffers.
- D3DXMESH_POINTS
- Use the D3DUSAGE_POINTS usage flag for vertex and
index buffers.
- D3DXMESH_RTPATCHES
- Use the D3DUSAGE_RTPATCHES usage flag for vertex
and index buffers.
- D3DXMESH_NPATCHES
- Specifying this flag causes the vertex and index
buffer of the mesh to be created with D3DUSAGE_NPATCHES flag. This is
required if the mesh object is to be rendered using N-patch enhancement
using Direct3D.
- D3DXMESH_VB_SYSTEMMEM
- Use the D3DPOOL_SYSTEMMEM usage flag for vertex
buffers.
- D3DXMESH_VB_MANAGED
- Use the D3DPOOL_MANAGED usage flag for vertex
buffers.
- D3DXMESH_VB_WRITEONLY
- Use the D3DUSAGE_WRITEONLY usage flag for vertex
buffers.
- D3DXMESH_VB_DYNAMIC
- Use the D3DUSAGE_DYNAMIC usage flag for vertex
buffers.
- D3DXMESH_VB_SOFTWAREPROCESSING
- Use the D3DUSAGE_SOFTWAREPROCESSING usage flag for
vertex buffers.
- D3DXMESH_IB_SYSTEMMEM
- Use the D3DPOOL_SYSTEMMEM usage flag for index
buffers.
- D3DXMESH_IB_MANAGED
- Use the D3DPOOL_MANAGED usage flag for index
buffers.
- D3DXMESH_IB_WRITEONLY
- Use the D3DUSAGE_WRITEONLY usage flag for index
buffers.
- D3DXMESH_IB_DYNAMIC
- Use the D3DUSAGE_DYNAMIC usage flag for index
buffers.
- D3DXMESH_IB_SOFTWAREPROCESSING
- Use the D3DUSAGE_SOFTWAREPROCESSING usage flag for
index buffers.
- D3DXMESH_VB_SHARE
- Forces the cloned meshes to share vertex buffers.
- D3DXMESH_USEHWONLY
- Use hardware processing only. For mixed-mode
device, this flag will cause the system to use hardware (if supported in
hardware) or will default to software processing.
- D3DXMESH_SYSTEMMEM
- Equivalent to specifying both
D3DXMESH_VB_SYSTEMMEM and D3DXMESH_IB_SYSTEMMEM.
- D3DXMESH_MANAGED
- Equivalent to specifying both D3DXMESH_VB_MANAGED
and D3DXMESH_IB_MANAGED.
- D3DXMESH_WRITEONLY
- Equivalent to specifying both
D3DXMESH_VB_WRITEONLY and D3DXMESH_IB_WRITEONLY.
- D3DXMESH_DYNAMIC
- Equivalent to specifying both D3DXMESH_VB_DYNAMIC
and D3DXMESH_IB_DYNAMIC.
- D3DXMESH_SOFTWAREPROCESSING
- Equivalent to specifying both
D3DXMESH_VB_SOFTWAREPROCESSING and D3DXMESH_IB_SOFTWAREPROCESSING.
Remarks
A 32-bit mesh (D3DXMESH_32BIT) can theoretically
support (2^32)-1 faces and vertices. However, allocating memory for a mesh that
large on a 32-bit operating system is not practical.
一般情況下,參數(shù)Option置為D3DMESH_SYSTEMMEM或D3DMESH_MANAGED,表示對(duì)Direct3D頂點(diǎn)緩沖區(qū)和索引緩沖區(qū)使用D3DPOOL_SYSTEMMEM或D3DPOOL_MANAGED內(nèi)存。
調(diào)用D3DXCreateMesh()函數(shù)創(chuàng)建了網(wǎng)格模型對(duì)象后,還需要為其載入模型數(shù)據(jù),而載入模型數(shù)據(jù)是比較復(fù)雜的。所以在大多數(shù)情況下,不直接調(diào)用該函數(shù),它被封裝在Direct3D擴(kuò)展實(shí)用庫(kù)函數(shù)中,由Direct3D在內(nèi)部完成網(wǎng)格模型對(duì)象的創(chuàng)建和模型數(shù)據(jù)的載入操作。
通過(guò).x文件生成網(wǎng)格模型
復(fù)雜的三維模型實(shí)際上是由許許多多的多邊形構(gòu)成的,所以首先需要得到這些構(gòu)成模型的多邊形。使用Direct3D功能擴(kuò)展庫(kù)函數(shù)D3DXLoadMeshFromX(),可以從.X文件中提取多邊形信息(包括頂點(diǎn)坐標(biāo)、顏色、法向量和紋理信息等),生成網(wǎng)格模型。該函數(shù)的聲明如下:
Loads a mesh from a DirectX .x file.
HRESULT D3DXLoadMeshFromX(
LPCTSTR pFilename,
DWORD Options,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXBUFFER * ppAdjacency,
LPD3DXBUFFER * ppMaterials,
LPD3DXBUFFER * ppEffectInstances,
DWORD * pNumMaterials,
LPD3DXMESH * ppMesh
);
Parameters
- pFilename
- [in] Pointer to a string that specifies the
filename. If the compiler settings require Unicode, the data type LPCTSTR
resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See
Remarks.
- Options
- [in] Combination of one or more flags from the
D3DXMESH enumeration, which specifies creation options for the mesh.
- pD3DDevice
- [in] Pointer to an IDirect3DDevice9 interface, the
device object associated with the mesh.
- ppAdjacency
- [out] Pointer to a buffer that contains adjacency
data. The adjacency data contains an array of three DWORDs per face that
specify the three neighbors for each face in the mesh. For more information
about accessing the buffer, see ID3DXBuffer.
- ppMaterials
- [out] Pointer to a buffer containing materials
data. The buffer contains an array of D3DXMATERIAL structures, containing
information from the DirectX file. For more information about accessing the
buffer, see ID3DXBuffer.
- ppEffectInstances
- [out] Pointer to a buffer containing an array of
effect instances, one per attribute group in the returned mesh. An effect
instance is a particular instance of state information used to initialize an
effect. See D3DXEFFECTINSTANCE. For more information about accessing the
buffer, see ID3DXBuffer.
- pNumMaterials
- [out] Pointer to the number of D3DXMATERIAL
structures in the ppMaterials array, when the method returns.
- ppMesh
- [out] Address of a pointer to an ID3DXMesh
interface, representing the loaded mesh.
Return Values
If the function succeeds, the return value is D3D_OK.
If the function fails, the return value can be one of the following values:
D3DERR_INVALIDCALL, E_OUTOFMEMORY.
Remarks
The compiler setting also determines the function
version. If Unicode is defined, the function call resolves to
D3DXLoadMeshFromXW. Otherwise, the function call resolves to D3DXLoadMeshFromXA
because ANSI strings are being used.
All the meshes in the file will be collapsed into one
output mesh. If the file contains a frame hierarchy, all the transformations
will be applied to the mesh.
For mesh files that do not contain effect instance
information, default effect instances will be generated from the material
information in the .x file. A default effect instance will have default values
that correspond to the members of the D3DMATERIAL9 structure.
The default texture name is also filled in, but is
handled differently. The name will be Texture0@Name, which corresponds to an
effect variable by the name of "Texture0" with an annotation called "Name." This
will contain the string file name for the texture.
LPD3DXBUFFER因數(shù)據(jù)操作的方便性而誕生,它的好處是可以存儲(chǔ)頂點(diǎn)位置坐標(biāo)、材質(zhì)、紋理等多種類(lèi)型的Direct3D數(shù)據(jù),而不必對(duì)每種數(shù)據(jù)聲明一種函數(shù)接口類(lèi)型。可使用接口函數(shù)ID3DXBuffer::GetBufferPointer()獲取緩沖區(qū)中的數(shù)據(jù),使用ID3DXBuffer::GetBufferSize()獲取緩沖區(qū)數(shù)據(jù)大小。
載入材質(zhì)和紋理
如果函數(shù)D3DXLoadMeshFromX()調(diào)用成功,那么參數(shù)ppMaterials就會(huì)獲取.x文件中三維模型導(dǎo)出的材質(zhì)和紋理信息,而pNumMaterials則會(huì)獲得材質(zhì)的數(shù)目,將材質(zhì)和紋理信息從中提取出來(lái)的代碼如下:
bool init_geometry()
{
ID3DXBuffer* material_buffer;
/*
D3DXLoadMeshFromXA(
LPCSTR pFilename,
DWORD Options,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXBUFFER *ppAdjacency,
LPD3DXBUFFER *ppMaterials,
LPD3DXBUFFER *ppEffectInstances,
DWORD *pNumMaterials,
LPD3DXMESH *ppMesh);
*/
if(FAILED(D3DXLoadMeshFromX("airplane.x", D3DXMESH_MANAGED, g_device, NULL, &material_buffer, NULL,
&g_num_materials, &g_mesh)))
{
MessageBox(NULL, "Could not find airplane.x", "ERROR", MB_OK);
return false;
}
D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();
g_mesh_materials = new D3DMATERIAL9[g_num_materials];
g_mesh_textures = new IDirect3DTexture9*[g_num_materials];
for(DWORD i = 0; i < g_num_materials; i++)
{
g_mesh_materials[i] = xmaterials[i].MatD3D;
// set ambient reflected coefficient, because .x file do not set it.
g_mesh_materials[i].Ambient = g_mesh_materials[i].Diffuse;
g_mesh_textures[i] = NULL;
if(xmaterials[i].pTextureFilename != NULL && strlen(xmaterials[i].pTextureFilename) > 0)
D3DXCreateTextureFromFile(g_device, xmaterials[i].pTextureFilename, &g_mesh_textures[i]);
}
material_buffer->Release();
return true;
}
一個(gè)三維網(wǎng)格模型通常是由幾個(gè)子模型組成的,在制作模型時(shí)通常為每個(gè)子模型分別設(shè)置材質(zhì)和紋理,所以這些子模型就可能使用不同的材質(zhì)和紋理,因此在Direct3D程序就需要為所有的子模型分別保存材質(zhì)和紋理。此外,因?yàn)槊總€(gè)子模型可能具有不同的材質(zhì)和紋理,所以在渲染三維模型時(shí)也需要逐個(gè)子模型分別進(jìn)行渲染。
渲染網(wǎng)格模型
網(wǎng)格模型接口ID3DXMesh實(shí)際上是三維物體的頂點(diǎn)緩沖區(qū)的集合,它將創(chuàng)建頂點(diǎn)緩沖區(qū)、定義靈活頂點(diǎn)格式和繪制頂點(diǎn)緩沖區(qū)等功能封裝在一個(gè)COM對(duì)象里,極大地方便了三維物體的繪制。對(duì)于以ID3DXMesh表示的三維物體,可以遍歷它所有的頂點(diǎn)緩沖區(qū),按照相應(yīng)的頂點(diǎn)格式將它們分別渲染,也可以直接調(diào)用它的接口函數(shù)ID3DXMesh::DrawSubset()繪制圖形,該函數(shù)的聲明如下:
Draws a subset of a mesh.
HRESULT DrawSubset(
DWORD AttribId
);
Parameters
- AttribId
- [in] DWORD that specifies which subset of the mesh
to draw. This value is used to differentiate faces in a mesh as belonging to
one or more attribute groups.
Return Values
If the method succeeds, the return value is D3D_OK. If
the method fails, the return value can be D3DERR_INVALIDCALL.
Remarks
The subset that is specified by AttribId will be
rendered by the IDirect3DDevice9::DrawIndexedPrimitive method, using the
D3DPT_TRIANGLELIST primitive type, so an index buffer must be properly
initialized.
An attribute table is used to identify areas of the
mesh that need to be drawn with different textures, render states, materials,
and so on. In addition, the application can use the attribute table to hide
portions of a mesh by not drawing a given attribute identifier (AttribId)
when drawing the frame.
渲染網(wǎng)格模型的代碼如下:
void render()
{
g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_X#050505, 1.0f, 0);
g_device->BeginScene();
setup_world_matrix();
for(DWORD i = 0; i < g_num_materials; i++)
{
g_device->SetMaterial(&g_mesh_materials[i]);
g_device->SetTexture(0, g_mesh_textures[i]);
g_mesh->DrawSubset(i);
}
g_device->EndScene();
g_device->Present(NULL, NULL, NULL, NULL);
}
示例程序運(yùn)行效果圖:

源程序:
#include <d3dx9.h>
#pragma warning(disable : 4127)
#define CLASS_NAME "GameApp"
#define release_com(p) do { if(p) { (p)->Release(); (p) = NULL; } } while(0)
IDirect3D9* g_d3d;
IDirect3DDevice9* g_device;
ID3DXMesh* g_mesh;
D3DMATERIAL9* g_mesh_materials;
IDirect3DTexture9** g_mesh_textures;
DWORD g_num_materials;
void setup_world_matrix()
{
D3DXMATRIX mat_world;
D3DXMatrixRotationY(&mat_world, timeGetTime() / 1000.0f);
g_device->SetTransform(D3DTS_WORLD, &mat_world);
}
void setup_view_proj_matrix()
{
// setup view matrix
D3DXVECTOR3 eye(0.0f, 10.0f, -20.0f);
D3DXVECTOR3 at(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX mat_view;
D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
g_device->SetTransform(D3DTS_VIEW, &mat_view);
// setup projection matrix
D3DXMATRIX mat_proj;
D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
g_device->SetTransform(D3DTS_PROJECTION, &mat_proj);
}
bool init_geometry()
{
ID3DXBuffer* material_buffer;
/*
D3DXLoadMeshFromXA(
LPCSTR pFilename,
DWORD Options,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXBUFFER *ppAdjacency,
LPD3DXBUFFER *ppMaterials,
LPD3DXBUFFER *ppEffectInstances,
DWORD *pNumMaterials,
LPD3DXMESH *ppMesh);
*/
if(FAILED(D3DXLoadMeshFromX("airplane.x", D3DXMESH_MANAGED, g_device, NULL, &material_buffer, NULL,
&g_num_materials, &g_mesh)))
{
MessageBox(NULL, "Could not find airplane.x", "ERROR", MB_OK);
return false;
}
D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();
g_mesh_materials = new D3DMATERIAL9[g_num_materials];
g_mesh_textures = new IDirect3DTexture9*[g_num_materials];
for(DWORD i = 0; i < g_num_materials; i++)
{
g_mesh_materials[i] = xmaterials[i].MatD3D;
// set ambient reflected coefficient, because .x file do not set it.
g_mesh_materials[i].Ambient = g_mesh_materials[i].Diffuse;
g_mesh_textures[i] = NULL;
if(xmaterials[i].pTextureFilename != NULL && strlen(xmaterials[i].pTextureFilename) > 0)
D3DXCreateTextureFromFile(g_device, xmaterials[i].pTextureFilename, &g_mesh_textures[i]);
}
material_buffer->Release();
return true;
}
bool init_d3d(HWND hwnd)
{
g_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if(g_d3d == NULL)
return false;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_device)))
{
return false;
}
if(! init_geometry())
return false;
setup_view_proj_matrix();
g_device->SetRenderState(D3DRS_LIGHTING, FALSE);
return true;
}
void cleanup()
{
delete[] g_mesh_materials;
if(g_mesh_textures)
{
for(DWORD i = 0; i < g_num_materials; i++)
release_com(g_mesh_textures[i]);
delete[] g_mesh_textures;
}
release_com(g_mesh);
release_com(g_device);
release_com(g_d3d);
}
void render()
{
g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(5, 5, 5), 1.0f, 0);
g_device->BeginScene();
setup_world_matrix();
for(DWORD i = 0; i < g_num_materials; i++)
{
g_device->SetMaterial(&g_mesh_materials[i]);
g_device->SetTexture(0, g_mesh_textures[i]);
g_mesh->DrawSubset(i);
}
g_device->EndScene();
g_device->Present(NULL, NULL, NULL, NULL);
}
LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_KEYDOWN:
if(wParam == VK_ESCAPE)
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR, INT)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = inst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = CLASS_NAME;
wc.hIconSm = NULL;
if(! RegisterClassEx(&wc))
return -1;
HWND hwnd = CreateWindow(CLASS_NAME, "Direct3D App", WS_OVERLAPPEDWINDOW, 200, 100, 640, 480,
NULL, NULL, wc.hInstance, NULL);
if(hwnd == NULL)
return -1;
if(init_d3d(hwnd))
{
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
render();
}
}
cleanup();
UnregisterClass(CLASS_NAME, wc.hInstance);
return 0;
}