这次的笔记包含两个内容: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* ppVertexRemap
LPD3DXMESH* 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 );
}
}
}
//优化Mesh
hr = g_pMesh->OptimizeInplace(
D3DXMESHOPT_ATTRSORT |
D3DXMESHOPT_COMPACT |
D3DXMESHOPT_VERTEXCACHE,
(DWORD*)pAdjacentBuffer->GetBufferPointer(),
0, 0, 0 );
这里!!!完整代码
圣斗士也有倒下的时候
但至少他为我们制造了无数经典的回忆
——致科比布莱恩特,希望早日回归