分享
 
 
 

Game Programming with DirectX -- 08[Mesh]

王朝other·作者佚名  2006-05-23
窄屏简体版  字體: |||超大  

第八集 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的基本使用.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有