D3D9学习笔记之基础几何体的深入应用(一)

网格(一)

在开始之前,先附上基础网格对象的基础应用:
D3DX几何体的基础应用

在上面的链接中对 ID3DXMesh 接口有了一些了解,现在开始进一步研究该接口,内容很大程度上是与 ID3DXMesh 接口相关的数据和方法的概览。

注意,ID3DXMesh 接口继承了其父接口 ID3DXBaseMesh 的大部分功能,这一点很重要,因为其它类型的网格接口,如 ID3DXPMesh(渐进网格)也继承自接口 ID3DXBaseMesh ,所以接下来凡是涉及到其它网格类型的主题也都是相关的。

学习目标:

  • 了解 ID3DXMesh 接口对象的内部数据组织形式
  • 了解如何创建 ID3DXMesh 接口的对象
  • 了解如何优化 ID3DXMesh 接口的对象
  • 了解如何绘制 ID3DXMesh 接口的对象

几何信息:
ID3DXBaseMesh 接口包含了有一个顶点缓存(用于存储网格顶点)和一个索引缓存(决定顶点以哪种顺序构成三角形单元),我们可用下列方法得到指向这些接口的指针

HRESULT ID3DXMesh::GetVertexBuffer(LPDIRECT3DVERTEXBUFFER9* ppVB);
HRESULT ID3DXMesh::GetIndexBuffer(LPDIRECT3DINDEXBUFFER9* ppIB);

//下面示范如何使用这些方法
IDirect3DVertexBuffer9* vb =0;
Mesh->GetVertexBuffer(&vb);

IDirect3DIndexBuffer9* id =0;
Mesh->GetIndexBuffer(&ib);

注意:ID3DXMesh 接口仅支持将索引化的三角形单元列表作为其基本类型

锁定读写操作:
如果想锁定这些缓存,然后进行读写操作,可使用如下函数对,注意,这些方法锁定的是整个顶点缓存或索引缓存

HRESULT ID3DXMesh::LockVertexBuffer(DWORD Flags, BYTE** ppData);
HRESULT ID3DXMesh::LockIndexBuffer(DWORD Flags, BYTE** ppData);
  • Flags: 该参数描述如何进行锁定,锁定标记到这里看
  • ppData: 该参数返回指向被锁定的内存的指针的地址

当对锁定内存完成操作后,必须调用相应的解锁方法:

HRESULT ID3DXMesh::UnlockVertexBuffer();
HRESULT ID3DXMesh::UnlockIndexBuffer();

下面是 ID3DXMesh 接口用于获取几何信息的另外一些方法:

  • DWORD GetFVF(); 返回一个描述了顶点格式的 DWORD 类型值
  • DWORD GetNumVertices(); 返回顶点缓存中的顶点个数
  • DWORD GetNumBytesPerVertex(); 返回每个顶点所占的字节数
  • DWORD GetNumFaces(); 返回网格中(三角形)面片的个数

子集和属性缓存

一个网格由一个或多个子集组成,一个子集是网格中一组可用相同属性进行绘制的三角形单元,这里的属性是指材质,纹理和绘制状态。比如:
房子有:地板,墙,天花板,窗户
subset0: 地板。用地板的属性绘制该子集内的三角形
subset1: 墙。用墙的属性绘制该子集内的三角形
subset2: 天花板。用天花板的属性绘制该子集内的三角形
subset3: 窗户。用窗户的属性绘制该子集内的三角形

为了区分不同的子集,我们为每个子集指定一个唯一的非负的整数值,该值可为 DWORD 类型所能容纳的任何非负整数。

网格中每个三角形单元都被赋予了一个属性 ID, 该 ID 指定了该三角形单元所属的子集。
这些三角形单元的属性 ID 被存储在网格的属性缓存中,该属性缓存实际上是一个 DWORD类型的数组,由于每个面片(即三角形)在属性缓存中都有对应项,所以属性缓存中元素的个数与网格中面片的个数完全相等。而且属性缓存中的那些项与在索引缓存中定义的三角形单元是一一对应的;即属性缓存中的第 i 项对应于索引缓存中的第 i 个三角形。三角形单元 i 是由索引缓存中如下3个索引定义的:
A = i * 3
B = i * 3+1
C = i * 3+2

要想访问属性缓存,必须首先将其锁定,参见下面的代码段:

WORD* buffer =0;
Mesh->LockAttributeBuffer(lockingFlags, &buffer);

//读或写属性缓冲区

Mesh->UnlockAttributeBuffer();

绘制

ID3DXMesh 接口提供了方法 DrawSubset(DWORD AttribId) 用于绘制由参数 AttribId 指定的子集中的三角形单元,例如,要想绘制子集0中的所有三角形单元,可这样做:

Mesh->DrawSubset(0);

若要绘制整个网格,必须绘制该网格的所有子集,比较方便的方法是将各子集的属性 ID 依次指定为 0,1,2,…, n-1,其中 n 为子集的总数,每个子集都有一个对应的材质和纹理数组,这样用索引 i 就可以找到与子集 i 相关的材质和纹理。这种指定方式也使我们能够用一个简单的循环完成整个网格的绘制。

for( int i=0; i<numSubsets; i++ )
{
	Device->SetMaterial( mtrls[i] );		//材质
	Device->SetTexture( 0, textures[i] );	//纹理
	Mesh->DrawSubset(i);					//绘制
}

网格优化

为了更高效的绘制一个网格,我们可对该网格中的定义和索引进行重组,这个重组的过程称为网格优化,我们可借助下面的方法来完成优化:

HRESULT ID3DXMesh::OptimizeInplace(
	DWORD Flags,
	CONST DWORD* pAdjacencyIn,
	DWORD* pAdjacencyOut,
	DWORD* pFaceRemap,
	LPD3DXBUFFER* ppVertexRemap
);
  • Flags: 优化选项标记,通知该方法所要实施的优化方案,该参数可选取下列选项中的一项或多项
  • D3DXMESHOPT_COMPACT 从网格中移除那些无用顶点和索引
  • D3DXMESHOPT_ATTSORT 依据属性对各三角形单元进行排序,并生成一个属性表,这样可使 DrawSubset 获得更高的绘制效率
  • D3DXMESHOPT_VERTEXCACHE 提高顶点高速缓存的命中率
  • D3DXMESHOPT_STRIPORDER 对索引进行重组,以使三角形单元条带尽可能的长
  • D3DXMESHOPT_IGNOREVERTS 仅对索引进行优化,忽略顶点
    注意:标记 D3DXMESHOPT_VERTEXCACHE 和 D3DXMESHOPT_STRIPREORDER 不应许被同时使用
  • pAdjacencyIn: 指向未经优化的网格的邻接数组的指针
  • pAdjacencyOut: 指向一个 DWORD 类型数组的指针,该数组被填充了经过优化后的网格的邻接信息,该数组的维数必须为 ID3DXMesh::GetNumFace()*3,如果您不需要该信息,可将该参数赋为0
  • pFaceRemap: 指向一个 DWORD 类型数组的指针,该数组填充了网格面片的重绘信息,该数组的维数应为 ID3DXMesh::GetNumFaces(),当对一个网格面实施优化后,其面片在索引缓存中可能发生了移动,该面片重绘信息表明了原始面片所被移动到的新位置,即 pFaceRemap 中的第 i 项保存了表示第 i 个原始面片被移动到哪里的面片索引。如果您不需要该信息,可将该参数赋为0
  • ppVertexRemap: 指向 ID3DXMesh 对象指针的地址,该对象中保存了顶点的重绘信息,该缓存所包含的顶点数应为 ID3DXMesh::GetNumFaces(),当网格面经过优化后,其顶点在索引缓存中的位置可能发生了变动,顶点重绘信息表明了原始顶点移动到的新位置,即 pFaceRemap 中的第 i 项保存了表示第 i 个原始顶点被移动到哪里的顶点索引,如果您不需要该值,将该参数赋为0
    调用示例:
//获取非优化网格的邻接信息
DWORD adjacencyInfo[Mesh->GetNumFaces()*3];
Mesh->GenerateAdjacency(0.0f, adjacencyInfo);

//保存优化邻接信息的数组
DWORD optimizedAdjacencyInfo[Mesh->GetNumFaces()*3];

Mesh->OptimizeInplace(
	D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_COMPACT|D3DXMESHOPT_VERTEXCACHE,
	adjacencyInfo,
	optimizedAdjacencyInfo,
	0,
	0
);

与上诉方法功能类似的另一个方法是 Optimize, 该方法将输出调用了该方法的网格对象优化后的版本,但是调用该方法的那个网格对线本身不会发生改变

HRESULT ID3DXMesh::Optimize(
	DWORD Flags,
	CONST DWORD* pAdjacencyIn,
	DWORD* pAdjacencyOut,
	DWORD* pFaceRemap,
	LPD3DXBUFFER* ppVertexRemap,
	LPD3DXMESH* ppOptMesh		//要输出的优化网格
);

属性表:

如果一个网格对象在优化处理时使用了 **D3DXMESHOPT_ATTRSORT ** 标记,则构成该网格的三角形面片就会依据其属性进行排序,这样属于特定子集的三角形面片就会被保存在顶点缓存或索引缓存中的一个连续储存空间内
注意:构成网格的面片和属性缓存中的内容都依据属性进行了排序,这样属于特定子集的面片就会被保存在顶点缓存或索引缓存中的一个连续存储空间内,现在我们就可以很容易地标出属于某个子集的三角面片的始末位置。索引缓存中的每个 Tri 存储块都表示组成该三角形单元的三个顶点。
除了可对面片进行排序外, D3DXMESHOPT_ATTRSORT 优化选项还将创建一个属性表,该属性表示一个 D3DXATTRIBUTERANGE 类型的结构数组,属性表中的每一个项都对应于网格的一个子集,并指定了该子集中面片的几何信息被存储在顶点缓存或索引缓存的哪一个存储块中。 D3DXATTRIBUTERANGE 结构的定义如下:

typedef struct _D3DXATTRIBUTERANGE
{
    DWORD AttribId;		//属性表标识符
    DWORD FaceStart;	//起始面
    DWORD FaceCount;	//面数
    DWORD VertexStart;	//起始顶点
    DWORD VertexCount;	//顶点计数
} D3DXATTRIBUTERANGE;
  • AttribId 子集的ID
  • FaceStart 一个大小为 FaceStart * 3 的偏移量,表明了属于该子集的三角形单元在索引缓存中的起始位置
  • FaceCount 该子集中面片的总数
  • VertexStart 一个表明了与子集相关的顶点在顶点缓存中起始位置的偏移量
  • VertexCount 该子集中的顶点总数

属性表创建之后,只要通过一次快速查找,就能找到属于该子集的全部面片,这样就使子集的绘制可以高效的完成,注意,没有属性表时,为绘制某个子集,就只能对整个属性缓存进行若干次线性查找以确定每个属于该子集的面的位置

为了访问一个网格面的属性表,我们可借助如下方法:

HRESULT ID3DXMesh::GetAttributeTable(
	D3DXATTRIBUTERANGE *pAttribTable,
	DWORD *pAttribTableSize
);

该方法完成两项工作:返回属性表中的属性个数和用属性数据填充 D3DXATTRIBUTERANGE 类型的结构数组
要想获取属性表中的元素个数,我们可将上诉方法的第一个参数取为 0
获取属性表元素个数:

DWORD numSubsets = 0;
Mesh->GetAttributeTable(0, & numSubsets);

一旦得到了属性表中的元素个数,我们就可用属性数据填充 D3DXATTRIBUTERANGE 类型的结构数组
获取属性数据:

D3DXATTRIBUTERANGE table = new D3DXATTRIBUTERANGE[numSubsets];
Mesh->GetAttributeTable(table, &numSubsets);

我们可直接使用方法 ID3DXMesh::SetAttributeTable 对属性表进行设置,下面的例子中设定了属性表共有12个子集

D3DXATTRIBUTERANGE attributeTable[12];
//...用数据填充attributetable数组
Mesh->SetAttributeTable(attributeTable, 12);

邻接信息

对于某些网格运算,如网格优化,需要知道对任意给定的三角形面片,哪些面片与其邻接,这些邻接信息都存储在网格的邻接数组中
邻接数组的类型为 DWORD ,其每一项都包含了一个表示网格中某个三角形面片的索引,例如,第 i 项是指由下列顶点构成的三角形面片:
A = i *3; B= i * 3 +1; C = i * 3 +2;
注意,如果邻接数组中某一项等于 ULONG_MAX = 4294 967 295,则表面网格中某一特定边没有邻接面片,我们也可将该项赋为 -1 来表示此种情形,因为将一个 DWORD 类型的变量赋值为 -1 与赋为 ULONG_MAX 等效,之所以会出现这种溢出情况,是由于 DWORD 类型与无符号 32 位整数类型等价

所以,邻接数组的维数必须为 ID3DXBaseMesh::GetNumFaces() * 3,网格中的每一三角形面片都有3个可能的邻接面片

许多 D3DX 网格创建函数都能够输出邻接信息,但我们也可以使用如下方法:
输出邻接信息:

HRESULT ID3DXMesh::GenerateAdjacency(
	FLOAT fEpsilon,
	DWORD* pAdjacency
);
  • fEpsilon: 一个很小的正整数,指定了在某种距离度量下,两个点接近到何种程度方可认为这两点为同一点
  • pAdjacency: 指向一个 DWORD 类型的数组的指针,该数组中存储了邻接信息
DWORD adjacencyInfo[Mesh->GetNumFaces() * 3];
Mesh->GenerateAdjacency(0.001f, adjacencyInfo);

克隆

有时我们需要生成网格数据的一个副本,这时可使用如下方法:

HRESULT ID3DXMesh::CloneMeshFVF(
	DWORD Options,
	DWORD FVF,
	LPDIRECT3DDEVICE9 pDevice,
	LPD3DXMESH* ppCloneMesh
);
  • Options 创建某网格副本时的创建标记或标记组合,标记选项的完整列表请参阅 SDK 文档中 D3DXMESH 枚举类型的相关部分,一些常用的标记如下:
    • D3DXMESH_32BIT 网格将使用 32 位索引
    • D3DXMESH_MANAGED 网格数据将被存储于托管内存池中
    • D3DXMESH_WRITEONLY 指定网格数据为只读
    • D3DXMESH_DYNAMIC 网格缓存将使用动态内存
  • FVF 所要创建的克隆网格的灵活顶点格式
  • pDevice 与所创建的克隆网格的灵活顶点格式
  • ppCloneMesh 输出所创建的克隆网格
    注意,该方法允许目标网格采用与源网格不同的创建选项和灵活顶点格式,例如,假定有一个灵活顶点格式为 FVF_XYZ 的网格,我们想要创建网格的一个副本,但想将其顶点格式指定为 D3DFVF_XYZ|D3DFVF_NORMAL,可以这样做:
//假设网格和设备有效
ID3DXMesh* clone = 0;
Mesh->CloneMeshFVF(
	Mesh->GetOptions(),				//使用与源网格相同的选项
	D3DFVF_XYZ|D3DFVF_NORMAL,		//指定克隆FVF
	Device,
	&clone
);

创建网格(D3DXCreateMeshFVF)

到目前为止,我们已经使用 D3DXCreate* 系列函数创建了一些网格对象,我们也可使用 D3DXCreateMeshFVF 函数创建一个 “空” 网格对象,这里的 “空” 网格对象是指我们我指定了网格的面片总数和顶点总数,然后由 D3DXCreateMeshFVF 函数为顶点缓存,索引缓存和属性缓存分配大小合适的内存。为网格缓存分配好内存后,我们就可手工填入网格的数据(即,我们必须将顶点,索引以及属性分别写入顶点缓存,索引缓存和属性缓存中)。
创建一个空网格:

HRESULT WINAPI D3DXCreateMeshFVF(
	DWORD NumFaces,
	DWORD NumVertices,
	DWORD Options,
	DWORD FVF,
	LPDIRECT3DDEVICE9 pDDevice,
	LPD3DXMESH* ppMesh
);
  • NumFaces 网格将具有的面片总数,该值必须大于0

  • NumVertices 网格将具有的顶点总数,该值必须大于零

  • Options 创建网格时所使用的创建标记,标记选项的完整列表请参阅 SDK 文档中与枚举类型 D3DXMESH 相关的部分,一些常用的标记如下:

    • D3DXMESH_32BIT 网格将使用 32 位索引
    • D3DXMESH_MANAGED 网格数据将被存储于托管内存池中
    • D3DXMESH_WRITEONLY 指定网格数据为只读
    • D3DXMESH_DYNAMIC 网格缓存将使用动态内存
  • FVF 所要创建的克隆网格的灵活顶点格式

  • pDevice 与改网格相关的设备指针

  • ppMesh 所创建的网格对象的指针

另外也可以用函数 D3DXCreateMesh 函数来创建空网格,该函数原型如下:

	DWORD NumFaces,
	DWORD NumVertices,
	DWORD Options,
	CONST LPD3DVERTEXELEMENT9* pDeclaration,
	LPDIRECT3DDEVICE9 pDDevice,
	LPD3DXMESH* ppMesh

该函数的参数与 D3DXCreateMeshFVF 方法类似,只是第4个参数不同,在该函数中,我们并未指定 FVF,而是用一个 D3DVERTEXELEMENT9 类型的结构数组来描述顶点数据的布局方式,我们在这里略去了对 D3DVERTEXELEMENT9 结构的讨论,请结合 SDK 文档自行研究,但是值得一提的是下面与之相关联的函数:

HRESULT D3DXDeclaratorFromFVF(
	DWORD FVF,											//输入格式
	D3DVERTEXELEMENT9 Declaration[MAX_FVF_DECL_SIZE]	//输出格式
);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值