DirectX学习笔记(十二):网格进阶之(XFile文件导入、渐进网格原理及外接体的)实现

XFile文件:


前面的十一篇的内容里,绘制物体的方式大多都是采用DX内部的接口或者函数。如果以这样的方式来开发一款3D游戏,十个程序员目测得有九个忍受不枯燥的任务。那么问题来了:怎么办?以前很难办,但是 现在解决这个问题的方法有了。通过3D建模软件:3Dmax 、maya、LightWave等可以帮助我们简化3D建模过程。

这些建模工具可以将网格数据(几何信息、材质、动画等)导入到文件中。这样,我们可以通过编写的文件读取程序来获取网格数据并在3D应用程序中使用。

这是一种可行的方案。但是呢。还有一种更简便的方法:这种方式就是XFile文件导入。


XFile文件是一种特殊的网格文件格式(扩展名.X)。XFile文件格式之所以使用比较方便,主要是因为它是DirextX定义的格式。所以该格式得到了DX的有力支持。在D3DX库中,提供了很多对XFile格式的文件进行加载和保存的函数。那么这就意味着,在使用XFile文件格式时,我们就不需要再自己编写文件读取程序来获取文件中的网格数据信息了。


现在问题来了:如何加载XFile文件?

我们可以使用以下函数来来加载XFile的几何数据对象:

HRESULT D3DXLoadMeshFromX(
	LPSTR pFilename,
	DWORD Options,
	LPDIRECT3DDEVICE9 pDevice,
	LPD3DXBUFFER* ppAdjacency,
	LPD3DXBUFFER* ppMaterials,
	LPD3DXBUFFER* ppEffectInstances,
	PDWORD pNumMaterials,
	LPD3DXMESH* ppMesh);


函数参数:

pFilename:索要加载的文件名。

Options:创建网格时所使用的创建标记。具体请查阅SDK文档。

pDevice:与该网格对象相关的设备指针。

ppAdjacency:返回一个ID3XBUffer对象,该对象包含了一个描述该网格对象的临街信息的DWORD类型的数组。

ppMaterials:返回一个ID3XBUffer对象,该对象包含了一个描述该网格材质数据的D3DXMATERIAL类型的结构数组。

ppEffectInstances:返回一个ID3XBUffer对象,该对象包含了一个D3DXEFFECTINSTANCE结构。

pNumMaterials:返回网格中的材质数目。

ppMesh:返回所创建的并且已经填充的XFile几何数据的ID3DXMesh对象。


D3DXLoadMeshFromX函数在加载完XFile 文件后,返回的D3DXMATERIAL结构数组中的第i项与第i个子集对应。所以我们只需要将子集按照0,1,...n-1的顺序进行标记。这样利用一个简单的循环就可以对全部子集进行绘制。从而完成整个网格的绘制。




渐进网格原理:


在程序运行时,绘制总是会耗费一些时间。为了能够尽量的减少绘制的时间,我们使用了很多方法,其中不得不提就是渐进网格(简单、粗暴)。


对于一个3D物体而言:如果它距离摄像机比较远,我们看到的物体表面就没有那么细致,甚至看到的是一堆堆的东西,这就意味着我们没有必要对物体进行很是细致的绘制,即:减少索要绘制的三角形单元。相反来说,如果一个物体距离摄像机较近,那么我们就需要很是细致的绘制。也就意味着绘制的三角形单元较多。在满足相应的条件下,我们就通过增加或者减少一个物体绘制时的三角形单元来控制绘制时间。我们这样做的目的很简单:提高效率,减少绘制的时间。


问题是:哪些三角形单元绘制,哪些三角形单元又不绘制呢?

这里我们引入绘制的顶点属性权值结构。顶点权值结构允许我们为顶点的每个可能的分量指定一个权值,如果某个分量的权值被赋值为0.0,就表明该分量无权值。顶点分量越大,在简化的过程中该顶点被移除的概率越小。

顶点属性权值结构:

typedef struct D3DXATTRIBUTEWEIGHTS{  
    FLOAT Position;  
    FLOAT Boundary;  
    FLOAT Normal;  
    FLOAT Diffuse;  
    FLOAT Specular;  
    FLOAT Texcoord[8];  
    FLOAT Tangent;  
    FLOAT Binormal;  
    } D3DXATTRIBUTEWEIGHTS,*LPD3DXATTRIBUTEWEIGHTS


权值结构的默认值:

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,};  

切记:除非必须要该变顶点的默认权值,否则就按照上面的就ok了。

现在还有一个问题:如果对于一个物体进行绘制时,要减少三角形单元的绘制数量或者说要增加三角形单元的绘制数量,我们必须要对其进行约束,以保证场景物体的视觉的真实感。直白来讲:就是如何限制减少或者增加三角形单元的绘制数量?(这个问题必须要重视,如果不限制的话,目测结果很可能造成绘制时物体堆成块的情况,这样物体的本来面貌就gg了)。

ID3DXPMesh接口提供了以下方法:

GetMaxFaces(VOID): //返回渐进网格三角形数可被指定的上限
GetMaxVertices(VOID)//放回渐进网格顶点数可被指定的上限
GetMinFaces(VOID)    //返回渐进网格三角形数可被指定的下限
GetMinVertices(VOID)  //返回渐进网格顶点数可被指定的下限
SetNumFaces(DWORD Faces) //允许我们设置网格的三角形数可被简化或者细化到的个数




外接体:


首先我们要明确外接体是干嘛的:外接体是用来加速可见性检测和碰撞检测的。下面举个简单的例子:
比如现在我在玩3D枪战游戏,敌人一枪打在了我的头部,伤害了我高贵的身体。ok现在我被爆头了,挂掉了。那么游戏应用程序怎么判定敌人的打到了我的身体了呢?按照常理来说应该是这样解决:从敌人的枪口发射射线,然后射线第一次碰到的是我高贵的身体的头部,然后我死了。看似简单,但是涉及到的计算很是耗费时间。比如:射线到了我的头部,为什么不是屁股?游戏中的人身上全是Mesh网格三角形单元,这个时候我就需要一个个将的三角形单元遍历,来判断到底是打到了屁股还是头上。于是可能发生的搞笑的事情来了:我被敌人爆头了,然后已经该死掉的我一枪把敌人爆头Shutdown(当然这种事是不可能发生的,高贵的程序员怎么可能让这种事情发生呢?)~~~~


那么我为了减少检测的时间,引入了外接体:

接着上面的例子:

比如:我在我身体的外面添加了一个外接体,那么我就只需要检测枪口发出的射线与这个外接体的检测就可以了。外接体的三角形网格相对与人物身上的三角形单元来说简直是少的很,那么这样做不就大大加快了遍历检测三角形单元的时间了吗?

这种方式是一种很好的近似。


那么怎么实现呢?

D3DX库中提供了一些函数来计算一个网格的外接球和外接体。这些函数接收一个顶点数组,然后根据这些顶点计算出外接体和外接球。

函数原型:


函数原型如下;  
HRESULT WINAPI D3DXComputeBoundingSphere(  
    const D3DXVECTOR3 *pFirstPosition, //指向的顶点数组 
    DWORD NumVertices,                      //顶点个数
    DWORD dwStride,                      //每个顶点的大小
    D3DXVECTOR3 *pCenter,               //返回外接球的球心位置
    FLOAT *pRadius                       //返回外接球的半径
);  
  
HRESULT WINAPI D3DXComputeBoundingBox(  
    const D3DXVECTOR3 *pFirstPosition,  
    DWORD NumVertices,  
    DWORD dwStride,  
    D3DXVECTOR3 *pMin,  //返回外接体的最小点
    D3DXVECTOR3 *pMax   //返回外接体的最大点
);  


XFile文件导入代码:


点击打开链接

运行效果(可旋转):






渐进网格代码:


点击打开链接


运行效果:

(通过A键和D键来控制网格的三角形单元绘制数量)



外接体代码:


点击打开链接

运行效果:



有问题的欢迎留言交流,一起努力,一起进步。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页