GameProgrammingwithDirectX--08[Mesh]
第八集 Mesh
构造简单的球的3D模型已经复杂化了, 如果用代码构造比球更复杂的3D模型就更难了, 还好有专业级的3D模型设计软件, 这些软件构造的3D模型在DirectX Graphics中称为Mesh, DirectX Graphics中对应的接口为,
ID3DXBaseMesh
| - ID3DXMesh
| - ID3DXPMesh
ID3DXSPMesh
ID3DXPatchMesh
可以在DirectX9c SDK中的Mesh Support in D3DX主题中找到有关Mesh的描述.
8.1 ID3DXMesh中的信息
8.1.1 顶点和顶点索引
Mesh中包含的物体模型实际还是由顶点及顶点索引组成的, Mesh只是起把顶点, 顶点索引, 纹理属性, 材质属性包装在一起的作用, 简单资源的统一管理.
我们可以类似创建顶点一样来创建Mesh, DirectX Graphics提供的函数为,
HRESULT D3DXCreateMesh(DWORD NumFaces,
DWORD NumVertices,
DWORD Options,
CONST LPD3DVERTEXELEMENT9 * pDeclaration,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXMESH * ppMesh);
HRESULT D3DXCreateMeshFVF(DWORD NumFaces,
DWORD NumVertices,
DWORD Options,
DWORD FVF,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXMESH * ppMesh);
其中D3DXCreateMeshFVF函数比较实用, 创建一个 长方体 的代码为,
// #define D3DFVF_MYVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
// LPDIRECT3DDEVICE9 m_pD3DDev;
// LPD3DXMESH m_pMesh;
D3DXCreateMeshFVF(12, 8, D3DXMESH_MANAGED,
D3DFVF_MYVERTEX, m_pD3DDev, &m_pMesh)
Directx Graphics 只是根据参数申请相应的内存, 长方体 的顶点值和顶点索引还是要由我们自己去填写, 在Mesh中包含以下函数,
HRESULT LockVertexBuffer(DWORD Flags, LPVOID * ppData);
HRESULT UnlockVertexBuffer();
HRESULT LockIndexBuffer(DWORD Flags, LPVOID * ppData);
HRESULT UnlockIndexBuffer();
我们填写 长方体 的顶点值和顶点索引,
INT nSize = sizeof(MYVERTEX) * 8;
MYVERTEX aVertex[ ] =
{
{-1.0f, -1.0f, -1.0f, D3DCOLOR_ARGB(0, 255, 0, 0 ) },
{-1.0f, 1.0f, -1.0f, D3DCOLOR_ARGB(0, 0, 0, 255) },
{ 1.0f, 1.0f, -1.0f, D3DCOLOR_ARGB(0, 0, 255, 0 ) },
{ 1.0f, -1.0f, -1.0f, D3DCOLOR_ARGB(0, 0, 0, 255) },
{-1.0f, -1.0f, 1.0f, D3DCOLOR_ARGB(0, 0, 255, 0 ) },
{-1.0f, 1.0f, 1.0f, D3DCOLOR_ARGB(0, 0, 0, 255) },
{ 1.0f, 1.0f, 1.0f, D3DCOLOR_ARGB(0, 0, 255, 0 ) },
{ 1.0f, -1.0f, 1.0f, D3DCOLOR_ARGB(0, 0, 0, 255) }
};
LPVOID pV = NULL;
if (FAILED(m_pMesh->LockVertexBuffer(0, &pV)))
{
return E_FAIL;
}
MoveMemory(pV, aVertex, nSize);
m_pMesh->UnlockVertexBuffer();
nSize = sizeof(WORD) * 36;
WORD aIndex[] =
{
0, 1, 2,
2, 3, 0,
4, 7, 6,
6, 5, 4,
0, 3, 7,
7, 4, 0,
3, 2, 6,
6, 7, 3,
2, 1, 5,
5, 6, 2,
1, 0, 4,
4, 5, 1
};
if (FAILED(m_pMesh->LockIndexBuffer(0, &pV)))
{
return E_FAIL;
}
MoveMemory(pV, aIndex, nSize);
m_pMesh->UnlockIndexBuffer();
DirectX Graphics 中提供了实用的基本模型创建函数,
HRESULT D3DXCreateBox(LPDIRECT3DDEVICE9 pDevice,
FLOAT Width,
FLOAT Height,
FLOAT Depth,
LPD3DXMESH * ppMesh,
LPD3DXBUFFER * ppAdjacency);
HRESULT D3DXCreateCylinder(LPDIRECT3DDEVICE9 pDevice,
FLOAT Radius1,
FLOAT Radius2,
FLOAT Length,
UINT Slices,
UINT Stacks,
LPD3DXMESH * ppMesh,
LPD3DXBUFFER * ppAdjacency);
HRESULT D3DXCreateSphere(LPDIRECT3DDEVICE9 pDevice,
FLOAT Radius,
UINT Slices,
UINT Stacks,
LPD3DXMESH * ppMesh,
LPD3DXBUFFER * ppAdjacency);
HRESULT D3DXCreateTeapot(LPDIRECT3DDEVICE9 pDevice,
LPD3DXMESH * ppMesh,
LPD3DXBUFFER * ppAdjacency);
HRESULT D3DXCreateTorus(LPDIRECT3DDEVICE9 pDevice,
FLOAT InnerRadius,
FLOAT OuterRadius,
UINT Sides,
UINT Rings,
LPD3DXMESH * ppMesh,
LPD3DXBUFFER * ppAdjacency);
注意这些函数创建的模型的中心轴是Z轴.
// 宽3.0, 高2.0, 厚1.0的长方体
D3DXCreateBox(m_pD3DDev, 3.0, 2.0, 1.0, &m_pMeshBox, NULL)
// 被分成16块的圆台, 上半径1.0, 底半径2.0, 高4.0, 圆台被分为2层
// 这个函数可创建棱台, 棱锥, 圆台, 圆锥...
D3DXCreateCylinder(m_pD3DDev, 1.0, 2.0, 4.0, 16, 2, &m_pMeshCylinder, NULL)
// 球模型, 上集有描述
D3DXCreateSphere(m_pD3DDev, 2.0, 8, 8, &m_pMeshSphere, NULL)
// 茶壶, 想不通DX和OpenGL都喜欢这东西?
D3DXCreateTeapot(m_pD3DDev, &m_pMeshTeapot, NULL)
// 甜甜圈
D3DXCreateTorus(m_pD3DDev, 1.0, 2.0, 8, 8, &m_pMeshTorus, NULL)
渲染模型时, 只需调用Mesh的DrawSubset函数就可以了,
m_pMeshBox->DrawSubset(0);
m_pMeshCylinder->DrawSubset(0);
m_pMeshSphere->DrawSubset(0);
m_pMeshTeapot->DrawSubset(0);
m_pMeshTorus->DrawSubset(0);
m_pMesh->DrawSubset(0);
8.1.2 属性表及Mesh的优化
Mesh可以包含模型的表面材质和纹理属性, 模型的不同部分的表面材质, 纹理属性或渲染状态可能是不同的, 于是Mesh的内部根据模型的材质和纹理属性将组成模型的三角形分成不同的子集(subset), 在同一子集中的三角形的表面材质, 纹理或渲染状态是相同的, 每个子集都有自己的属性描述, 将这些属性描述合在一起就是属性表了,属性表中包含的元素的数量对应的就是Mesh中子集的数量.
Attribute Buffer Index Buffer Texture & Material Buffer
[ 0 ]---------+--[ 0 ]--+-----------[ 0 ]---+
[ 1 ]----+ | [ 1 ] | +------[ 1 ] |
[ 2 ]-+ | | [ ... ] | | [ ... ] |
[ ... ] | | +--[ 7 ]--+ | |
| +-------[ 8 ]-------+ |
| | [ ... ] | |
| +-------[ 15 ]-------+ |
+----------[ 16 ]------------------------------+
| { ... ] |
+----------[ 23 ]------------------------------+
[ ... ]
图8.1
在图8.1中, Mesh中共有3个subset,其中subset0 和 subset2 使用相同的表面材质和纹理, 那么是否可以通过重新定义顶点索引把这两个subset合并呢? --- Mesh的优化.
Mesh优化的主要目的是为了渲染时更有效率, 可以根据模型的特点选择优化的方式,
HRESULT Optimize(DWORD Flags,
CONST DWORD * pAdjacencyIn,
DWORD * pAdjacencyOut,
DWORD * pFaceRemap,
LPD3DXBUFFER * ppVertexRemap,
LPD3DXMESH * ppOptMesh);
HRESULT OptimizeInplace(DWORD Flags,
CONST DWORD * pAdjacencyIn,
DWORD * pAdjacencyOut,
DWORD * pFaceRemap,
LPD3DXBUFFER * ppVertexRemap);
参考DirectX9c SDK中对两个函数的详细说明. 如果把图8.1中的subset0 和 subset2合并, Flags参数中需要包含D3DXMESHOPT_ATTRSORT.
现在我们把前面创建的 长方体分成2个subset, 在渲染时使用不同的明暗处理,
DWORD dwSize = 0L;
LPD3DXATTRIBUTERANGE pRange = new D3DXATTRIBUTERANGE[2];
if (pRange != NULL)
{
m_pMesh->GetAttributeTable(pRange, &dwSize);
// 看看修改前的Mesh的属性表内容...
pRange[0].AttribId = 0;
pRange[0].FaceStart = 0;
pRange[0].FaceCount = 6;
pRange[0].VertexStart = 0;
pRange[0].VertexCount = 8;
pRange[1].AttribId = 1;
pRange[1].FaceStart = 6;
pRange[1].FaceCount = 6;
pRange[1].VertexStart = 0;
pRange[1].VertexCount = 8;
m_pMesh->SetAttributeTable(pRange, 2);
delete[] pRange;
}
渲染模型时,调用Mesh的DrawSubset函数, DrawSubset函数的参数就是Mesh中子集的序号
m_pD3DDev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
m_pMesh->DrawSubset(0);
m_pD3DDev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
m_pMesh->DrawSubset(1);
8.2 ID3DXMesh的存储及读取
8.2.1 Mesh的存储
3D建模软件在存储模型时使用的存储格式是不相同的, DirectX Graphics只支持自己的存储文件格式X file, 可以参考DirectX9c SDK中X File Format Reference主题.现在把前面创建的所有模型都存成文件, 需要的函数是,
HRESULT D3DXSaveMeshToX(LPCTSTR pFilename,
LPD3DXMESH pMesh,
CONST DWORD * pAdjacency,
CONST D3DXMATERIAL * pMaterials,
CONST D3DXEFFECTINSTANCE * pEffectInstances,
DWORD NumMaterials,
DWORD Format);
D3DXSaveMeshToX(_T(".//1.x"), m_pMeshBox, NULL, NULL, NULL, 0, D3DXF_FILEFORMAT_TEXT);
D3DXSaveMeshToX(_T(".//2.x"), m_pMeshCylinder, NULL, NULL, NULL, 0, D3DXF_FILEFORMAT_TEXT);
D3DXSaveMeshToX(_T(".//3.x"), m_pMeshSphere, NULL, NULL, NULL, 0, D3DXF_FILEFORMAT_TEXT);
D3DXSaveMeshToX(_T(".//4.x"), m_pMeshTeapot, NULL, NULL, NULL, 0, D3DXF_FILEFORMAT_TEXT);
D3DXSaveMeshToX(_T(".//5.x"), m_pMeshTorus, NULL, NULL, NULL, 0, D3DXF_FILEFORMAT_TEXT);
D3DXSaveMeshToX(_T(".//6.x"), m_pMesh, NULL, NULL, NULL, 0, D3DXF_FILEFORMAT_TEXT);
8.2.2 Mesh的读取
从X文件读取数据, 创建相应的Mesh是最常见的方式, 也可以从可执行文件中的资源中读取X文件数据, 另外象Blizzard的游戏总把N多资源放在同一文件中, 运行时只需load这个文件, 这时我们是从内存中读取X文件数据的.
HRESULT D3DXLoadMeshFromX(LPCTSTR pFilename,
DWORD Options,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXBUFFER * ppAdjacency,
LPD3DXBUFFER * ppMaterials,
LPD3DXBUFFER * ppEffectInstances,
DWORD * pNumMaterials,
LPD3DXMESH * ppMesh);
HRESULT D3DXLoadMeshFromXResource(HMODULE Module,
LPCSTR Name,
LPCSTR Type,
DWORD Options,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXBUFFER * ppAdjacency,
LPD3DXBUFFER * ppMaterials,
LPD3DXBUFFER * ppEffectInstances,
DWORD * pNumMaterials,
LPD3DXMESH * ppMesh);
HRESULT D3DXLoadMeshFromXInMemory(LPCVOID Memory,
DWORD SizeOfMemory,
DWORD Options,
LPDIRECT3DDEVICE9 pD3DDevice,
LPD3DXBUFFER * ppAdjacency,
LPD3DXBUFFER * ppMaterials,
LPD3DXBUFFER * ppEffectInstances,
DWORD * pNumMaterials,
LPD3DXMESH * ppMesh);
其中Mesh模型使用的材质或纹理在ppMaterials指向的缓冲内存中, 数据以D3DXMATERIAL结构的方式组织; pNumMaterials指向的是Mesh的子集个数, 同时也是ppMaterials指向的缓冲内存中D3DXMATERIAL的个数,
LPD3DXBUFFER pBuffer = NULL;
LPD3DXMESH pMesh = NULL;
if (FAILED(D3DXLoadMeshFromX(pXFile, D3DXMESH_SYSTEMMEM, m_pD3DDev, NULL,
&pBuffer, NULL, &m_dwCount, &pMesh)))
{
return E_FAIL;
}
m_ppD3DTexture = new LPDIRECT3DTEXTURE9[m_dwCount];
m_pD3DMaterial = new D3DMATERIAL9[m_dwCount];
if ((m_ppD3DTexture == NULL) || (m_pD3DMaterial == NULL))
{
pBuffer->Release();
pMesh->Release();
return E_FAIL;
}
LPD3DXMATERIAL pMaterial = LPD3DXMATERIAL(pBuffer->GetBufferPointer());
WCHAR szTemp[512] = { 0 };
for (DWORD i = 0L; i < m_dwCount; i++)
{
m_pD3DMaterial[i] = pMaterial[i].MatD3D;
// 使用Unicode Character Set时, 需要转换
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pMaterial[i].pTextureFilename,
-1, szTemp, 512);
if (FAILED(D3DXCreateTextureFromFile(m_pD3DDev, szTemp, &(m_ppD3DTexture[i]))))
{
m_ppD3DTexture[i] = NULL;
}
}
pBuffer->Release();
// 使用CloneMeshFVF可以创建我们需要的FVF类型的Mesh
if (FAILED(pMesh->CloneMeshFVF(D3DXMESH_MANAGED, D3DFVF_MYVERTEXTEX, m_pD3DDev, &m_pMesh)))
{
pMesh->Release();
return E_FAIL;
}
pMesh->Release();
// 防止原来的Mesh中没有包含顶点法向量的情况, Clone的Mesh再计算一次顶点法向量
D3DXComputeNormals(m_pMesh, NULL);
在渲染这个Mesh时, 不同的subset要提前设置不同的材质和纹理,
for (DWORD i = 0L; i < m_dwCount; i++)
{
m_pD3DDev->SetMaterial(&(m_pD3DMaterial[i]));
m_pD3DDev->SetTexture(0, m_ppD3DTexture[i]);
m_pMesh->DrawSubset(i);
}
8.3 ID3DXPMesh
ID3DXPMesh(ID3DXSPMesh)的特点和mip-map类似, 可以根据Mesh离视点的远近距离设置不同精细层次(levels of detail, LOD). 例如一个球离视点很近时是 64 * 64[块数 * 层数], 中等距离 16 * 16, 较远时 4 * 4, ... ID3DXPMesh是在ID3DXMesh的基础上创建的,
HRESULT D3DXGeneratePMesh(LPD3DXMESH pMesh,
CONST DWORD * pAdjacency,
CONST D3DXATTRIBUTEWEIGHTS * pAttr,
CONST FLOAT * pVertexWeights,
DWORD MinValue,
DWORD Options,
LPD3DXPMESH * ppPMesh);
pAttr是指向一个描述顶点权重的数据结构, 权重越大顶点被移除的概率越小, 默认的权重数据结构为,
D3DXATTRIBUTEWEIGHTS AttributeWeights;
AttributeWeights.Position = 1.0;
AttributeWeights.Boundary = 1.0;
AttributeWeights.Normal = 1.0;
AttributeWeights.Diffuse = 0.0;
AttributeWeights.Specular = 0.0;
AttributeWeights.Tex[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
在创建ID3DXPMesh后, 调用下面的函数来来设置面数或顶点数, 具体调用哪个函数要和D3DXGeneratePMesh函数中的参数Options一致.
HRESULT SetNumFaces(DWORD Faces);
HRESULT SetNumVertices(DWORD Vertices);
DWORD GetMaxFaces();
DWORD GetMinFaces();
DWORD GetMaxVertices();
DWORD GetMinVertices();
能设置的Faces或Verteices的取值区间分别为[GetMinFaces(), GetMaxFaces()]和[GetMinVertices(), GetMaxVertices()].
8.4 Mesh的例子
8.4.1 game8 project代码更新
game8 中只是演示了DirectX Graphics提供的基本模型构造函数的用法, ID3DXPMesh模型根据面数的增加或减少呈现的不同.
---------------------------------------------------------------
LPD3DXBUFFER pAdj = NULL;
LPD3DXBUFFER pBuffer = NULL;
LPD3DXMESH pMesh = NULL;
if (FAILED(D3DXLoadMeshFromX(pXFile, D3DXMESH_SYSTEMMEM, m_pD3DDev, &pAdj, &pBuffer, NULL, &m_dwCount, &pMesh)))
{
return E_FAIL;
}
m_ppD3DTexture = new LPDIRECT3DTEXTURE9[m_dwCount];
m_pD3DMaterial = new D3DMATERIAL9[m_dwCount];
if ((m_ppD3DTexture == NULL) || (m_pD3DMaterial == NULL))
{
pBuffer->Release();
pMesh->Release();
return E_FAIL;
}
LPD3DXMATERIAL pMaterial = LPD3DXMATERIAL(pBuffer->GetBufferPointer());
WCHAR szTemp[512] = { 0 };
for (DWORD i = 0L; i < m_dwCount; i++)
{
m_pD3DMaterial[i] = pMaterial[i].MatD3D;
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pMaterial[i].pTextureFilename, -1, szTemp, 512);
if (FAILED(D3DXCreateTextureFromFile(m_pD3DDev, szTemp, &(m_ppD3DTexture[i]))))
{
m_ppD3DTexture[i] = NULL;
}
}
pBuffer->Release();
if (FAILED(D3DXGeneratePMesh(pMesh, LPDWORD(pAdj->GetBufferPointer()), 0, 0, 1, D3DXMESHSIMP_FACE, &m_pMesh)))
{
pMesh->Release();
pAdj->Release();
return E_FAIL;
}
pMesh->Release();
pAdj->Release();
D3DXComputeNormals(m_pMesh, NULL);
m_lMinFace = DWORD(m_pMesh->GetMinFaces());
m_lMaxFace = DWORD(m_pMesh->GetMaxFaces());
m_pMesh->SetNumFaces(DWORD(m_lMaxFace));
m_lCurFace = m_lMaxFace;
---------------------------------------------------------------
8.4.2 game8 project说明
例子中没有光照, 我们只是简单的演示, 上下方向键控制视点在Y轴的移动, 左右方向键控制视点在X轴的移动, Page Up 增加ID3DXPMesh模型面数, Page Down减少ID3DXPMesh模型根据面数, 空格切换自动旋转, ENTER保存模型.
第八集 小结
这一集我们学习了要进行DirectX Graphics 3D编程中的Mesh的基本使用.
分享到:
相关推荐
Introduction to 3D Game Programming with DirectX 11 源码-上
这是龙书第二版 《 Introduction to 3D Game Programming with DirectX 9.0c: A Shader Approach 》 源码。
Introduction to 3D Game Programming with DirectX 11-12,龙书11,龙书12,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除。
Introduction to 3D Game Programming with DirectX 11 源码-下
Introduction to 3D Game Programming with DirectX 11 源码-中
经典的DX11龙书,作者依然是Frank Luna,非常值得一看
Introduction to 3D Game Programming with DirectX 9.0
龙书10中文版,译者汤毅。前言 这是一本介绍Direct3D 10交互式计算机图形编程的书,主要侧重于游戏开发。它涵盖了 Direct3D与着色器编程的基础知识,读者在掌握这些内容之后,将有能力阅读更高级的技 ...
with an emphasis on game development, using Direct3D 12. It teaches the fundamentals of Direct3D and shader programming, after which the reader will be prepared to go on and learn more advanced ...
<<Introduction to 3D Game Programming with DirectX >>9 的源码
Advanced 3D Game Programming With Directx 9.0 With Code.rar
美国游戏编程大师Allen Sherrod编写的Ultimate Game Programming with DirectX(第一版)的所有源代码。这本书围绕一个FPS游戏进行讲述,最后得到一个比较简单的3D的FPS游戏。英文版的电子书网上找不到,不过代码...
Introduction to 3D Game Programming with DirectX 9.01 Introduction to 3D Game Programming with DirectX 9.01 Introduction to 3D Game Programming with DirectX 9.01 Introduction to 3D Game Programming ...
源码 Introduction to 3D Game Programming with Directx12
Introduction to 3D Game Programming with DirectX 9.0c Shader Approach 源代码 This book presents an introduction to programming interactive computer graphics, with an emphasis on game development, ...
Introduction to 3D Game Programming with DirectX 11英文原版 无水印
共有三个压缩包 分三次上传 d
Introduction to 3D Game Programming with DirectX 12 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除
This updated bestseller provides an introduction to programming interactive computer graphics, with an emphasis on game development using DirectX 11. The book is divided into three main parts: basic ...