【Directx3D游戏开发】——网格模型(mesh .x)

来源:互联网 发布:日本买房知乎 编辑:程序博客网 时间:2024/05/12 20:47

这次的笔记包含两个内容:1:简单介绍网格模型 和 .x文件;2、优化Mesh模型已取得更高的渲染效率,用Direct3D去加载一个.X文件。


游戏中的场景和角色都非常复杂,想要手工去填充这些模型的每一个顶点几乎是个不可能的事情。一般是用建模工具去完成,然后导出一个程序可以识别的网格模型。最常用的是3DMax,和Maya。

1:.X模型

X格式是微软提供的一种开放的数据存储格式。.X支持自定义模版结构,可以通过它存储自己的任何一种数据,并不一定使我们的模型数据。当然现在要讨论的是.X格式在Direct3D的应用,DirectX的D3DX库提供丰富的支持函数,使得用户可以轻松的使用.X文件。并且.X文件还支持动画。DirectX Viewer 是一个.X文件的查看器,用于查看模型:

 唔之艾瑞莉娅:艾欧尼亚不会灭亡。

.X文件可以用文本格式存储,可以用记事本打开一看究竟,详细参考《Direct3D 游戏看法详解 》,同样可以采用二进制格式存储,并支持自定义文件结构。

2:D3DX库中一些相关接口

a)、ID3DXMesh 派生自ID3DXBaseMesh,包含了网格(Mesh)模型的集合信息,顶点缓存和索引缓存(描述顶点以何种方式组织成三角形)。相关函数:

//获得顶点缓冲区和索引缓冲区的指针HRESULT ID3DXMesh::GetVertexBuffer( LPDIRECT3DVERTEXBUFFER* ppVB ); HRESULT ID3DXMesh::GetIndexBuffer( LPDIRECT3DVERTEXBUFFER* ppVB );//锁定缓冲区,一遍进行读写操作HRESULT ID3DXMesh::LockVertexBuffer( DWORD Flags, BYTE** ppData );HRESULT ID3DXMesh::LockIndexBuffer( DWORD Flags, BYTE** ppData );//完成了相应操作,必须解锁HRESULT ID3DXMesh::UnlockVertexBuffer();HRESULT ID3DXMesh::UnlockIndexBuffer();//更多获取几何信息的方法DWORD GetFVF(); //返回顶点格式DWORD GetNumVertices() //返回顶点缓存中的定点数DWORD GetNumBytesPerVertex(); //返回每个顶点所占的字节数DWORD GetNumFaces(); //返回网格中(三角形)片面的个数。

3、网格(Mesh)结构

一个网格由一个或多个子集组成。一个子集是网格中一组可用相同属性进行绘制的三角形单元。实行指的是材质,纹理,绘制状态。

为了区分不同子集,给每个子集分配了不同的DWORD类型的ID.这些ID被存放在一个DWORD数组里面,也就是属性缓存。属性缓存的第i行与索引缓存中的第i个三角形单元。

访问索引缓存的接口:

DWORD* buffer = NULL;
Mesh->LockAttributeBuffer( lockFlags, &buffer );//读写操作Mesh->UnlockAttributeBuffer();

4、绘制整个网格(mesh)模型

只需要简单的去遍历绘制每一个子集:DrawSubset( i );

for(int i = 0; i < numSubsets;i++){//设置材质纹理Device->SetMaterial( Mtrl[i] );Device->SetTexture( 0, textuer[i] );Mesh->DrawSubset( i );}

5、优化:为了可以更加高效的绘制一个网格,需要对网格中的顶点和索引进行重组。有两个有优化函数,只有微小的区别。

HRESULT ID3DXMesh::OptimizeInplace(DWORD Flags, CONST DWORD* pAdjacencyIn,DWORD* pAdjacencyOut,DWORD* pFaceRemap,LPD3DXBUFFER* ppVertexRemap);HRESULT ID3DXMesh::Optimize(DWORD Flags, CONST DWORD* pAdjacencyIn,DWORD* pAdjacencyOut,DWORD* pFaceRemap,LPD3DXBUFFER* ppVertexRemapLPD3DXMESH* ppOptMesh);

从其中的函数名也可以知道它们之间的差别,OptimizeInplace()就在原Mesh模型上优化,Optimize()是输出一个新的模型,源模型并不会改变。

Flag:优化选项,可以是以下常量的组合

D3DXMESHOPT_COMPACT:从网格中移除那些无用的顶点和索引。

D3DXMESHOPT_ATTSORT:依据属性对个三角形进行排序,并生成一个属性表。这样可使DrawSubset更有效率

D3DXMESHOPT_VERTEXCACHE:提供顶点高速缓存的命中率

D3DXMESHOPT_STRIPORDER:对索引进行重组,以使三角形单元链尽可能长

D3DXMESHOPT_IGNOREVERTS:仅对索引进行优化,忽略顶点。

其中D3DXMESHOPT_VERTEXCACHE不能与D3DXMESHOPT_STRIPORDER同时使用。

pAdjacencyIn:输入的未经优化的链接数组指针

pAdjacencyOut:输出的优化后的链接数组指针,大小为ID3DXMesh::GetNumFaces()*3

6、关于IDXBuffer

这是一个COM接口,一种泛型的数据结构,作用是将数据存储在一个连续的内存块中。相关的两个方法:

LPVOID GetBufferPointer(); 返回缓存中的数据起始指针,需要进行指针类型转换。

DWORD GetBufferSize();返回缓存大小,单位为字节。

7、加载Mesh模型(.x文件)四步曲:

一、重文件中加载数据:

//从文件中加载Mesh模型hr = D3DXLoadMeshFromX( XFILE_NAME, D3DXMESH_MANAGED, g_pD3DDevice,&pAdjacentBuffer, &pMaterialBuffer,0, &numMaterials, &g_pMesh );
二、读取模型材质,纹理信息:

if( pMaterialBuffer != NULL && numMaterials != NULL ){//获取缓冲区地址D3DXMATERIAL* pMaterials = ( D3DXMATERIAL* )pMaterialBuffer->GetBufferPointer();//复制材质,和加载纹理for(DWORD i = 0;i < numMaterials; i++){pMaterials[i].MatD3D.Ambient = pMaterials[i].MatD3D.Diffuse;//复制材质用于Render()materials.push_back( pMaterials[i].MatD3D );//从文件中加载纹理if( pMaterials[i].pTextureFilename != NULL){IDirect3DTexture9* tex = NULL;hr = D3DXCreateTextureFromFileA( g_pD3DDevice, pMaterials[i].pTextureFilename,&tex );if( FAILED( hr ) ){MessageBox( 0, L"D3DXCreateTextureFromFile() - FAILED ", 0, MB_OK );return E_FAIL;}textures.push_back( tex );}else{//没有材质textures.push_back( NULL );}}}

三、优化网格:

//优化Meshhr = g_pMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT |D3DXMESHOPT_COMPACT |D3DXMESHOPT_VERTEXCACHE,(DWORD*)pAdjacentBuffer->GetBufferPointer(),0, 0, 0 );

四、设置相关变换(代码略)


这里!!!完整代码


圣斗士也有倒下的时候


但至少他为我们制造了无数经典的回忆


——致科比布莱恩特,希望早日回归