【Visual C++】游戏开发笔记三十七 浅墨DirectX提高班之五 顶点缓存的红颜知己:索引缓存的故事

 

 

本系列文章由zhmxy555(毛星云)编写,转载请注明出处。  

文章链接: http://blog.csdn.net/zhmxy555/article/details/8304741

作者:毛星云(浅墨)    邮箱: [email protected]   

 

 

本篇文章里,我们首先对索引缓存的相关概念进行了详细的剖析,然后介绍了索引缓存配合顶点缓存一起绘制图形的具体方法,最后依旧是提供文章配套的详细注释的源代码的欣赏,并在文章末尾提供了源代码下载。

 

在上篇文章《【Visual C++】游戏开发笔记三十六 浅墨DirectX提高班之四 顶点缓存的逆袭》里我们介绍了用顶点缓存来玩转Direct3D,绘制图形的方法。但是,在使用过程中我们会发现,当我们要绘制复杂几何体的时候,单单地运用顶点缓存来存储顶点数据的话,就显得有些死板和力不从心了。首先呢,让我们来举一些引例,证明我们提出的这个论点,引出这篇文章的主角。

 

 

一.引言

 

 

第一个例子。比如,我们现在要用顶点缓存绘制一个正方形。首先我们知道,三角形是Direct3D中绘制图形的基本单元,我们绘制任何图形,说白了,就是用大量的三角形组合起来,堆砌完成的。而正方形,显然是由两个大小相同的三角形结合起来组成的。所以要绘制一个正方形,我们用顶点缓存写两个三角形,然后进行绘制就可以了。而一个三角形有三个顶点,两个三角形就有六个顶点。所以,用顶点缓存绘制一个正方形的话,需要用六个顶点缓存。而众所周知一个正方形也就是四个顶点。也就是说我们单用顶点缓存来绘制一个正方形,多用了两个顶点。下面就是我们在上篇文章中用到的配图:

 

其实单单用顶点缓存来绘制图形的话,需要的个数是非常好算的,我们只要数图形中三角形的个数,然后把这个个数乘以3就可以了。

第二个例子,八边形的绘制。如下图,单单用顶点缓存来绘制八边形的话,需要3*8=24个顶点缓存。

代码方面,我们就可以这样写:  

 

Vertex circle[24] = {
      v0, v1, v2,   // Triangle 0
      v0, v2, v3,   // Triangle 1
      v0, v3, v4,   // Triangle 2
      v0, v4, v5,   // Triangle 3
      v0, v5, v6,   // Triangle 4
      v0, v6, v7,   // Triangle 5
      v0, v7, v8,   // Triangle 6
      v0, v8, v1    // Triangle 7
};

 

 

第三个例子,立方体的绘制。如下图

我们知道,一个立方体六个面,每个面都为一个正方形,所以一共有2*6=12个三角形,所以就有12*3=36个顶点。则单单用顶点缓存来完成这个立方体的话,就需要36个顶点缓存。

一个立方体只有8个顶点,我们的想法是每个顶点我们只要记录一次,这样就可以节约我们的资源开销。但是我们采用的这种方式,却需要36个顶点,每个顶点都多存储了3.5次,这不科学。

通过上面的几个引例我们可以发现,这种单单用顶点缓存来绘制图形的方法在应对复杂图形的时候非常不科学,显得复杂而力不从心。

也就是说,当物体模型很复杂、顶点数量很大时,仅使用顶点缓存绘制图形会使重复的顶点大大增加,并且Direct3D仍需要对这些重复的顶点进行计算,因此需要更多的存储空间和更大的开销。

这时候,我们顶点缓存的红颜知己——索引缓存是时候出场了。

 

索引缓存((Index Buffers)),人如其名,它就是一个索引,用于记录顶点缓存中每一个顶点的索引位置。我们可以把索引缓存理解为是一本书(这本书就是顶点缓存)的目录,一本书有了目录,我们才能高屋建瓴,更快更高效地去阅读和掌握这本书。索引缓存作为顶点缓存的目录和提纲,能让顶点缓存发挥出内在的潜力,更高效更容易地绘制出一个3D图形来。

 另外提一点,索引缓存能够加速显存中快速存取顶点数据位置的能力。

 

 

 

 

 

二、索引缓存的使用思路

 

 

 

顶点缓存保存了物体模型所有的顶点数据,而这些数据可以是唯一的。索引缓存保存了构成物体的顶点在顶点缓存的索引值,通过索引查找对应的顶点,以完成图形的绘制。

下面我们看看上篇文章里我们介绍顶点缓存时贴出来过的绘制一个正方形需要的思路图的升级版,这幅图是采用顶点缓存和索引缓存双剑合璧的方法来绘制的,可以和文章开头举得第一个例子的图对照起来看:

 

我们可以看到,在这幅图中我们用4个顶点和6个索引来描述一个正方形。

 

我们在顶点缓存中只需要保存这四个顶点就可以了,而绘制正方形的两个三角形△V0V1V2和△V0V2V3则通过索引缓存来表示。

关于上面讲到的知识,我们再不厌其烦地总结起来一遍:

当物体模型很复杂、顶点数量很大时,仅使用顶点缓存绘制图形会使重复的顶点大大增加,并且Direct3D仍需要对这些重复的顶点进行计算,因此需要更多的存储空间和更大的开销。但是如果我们把顶点缓存和他的好兄弟索引缓存配合起来使用的话,就可以化繁为简,化腐朽为神奇,不仅我们代码敲起来轻松了很多,也让我们写出来的程序性能可以很高。越复杂的图形,越体现了索引缓存的价值。不过需要注意的是,我们后面使用Direct3D绘制的物体,大多数情况下是从3D模型文件中载入的,而不是在Direct3D中用代码敲出来的。

 

 

 

三、相濡以沫的顶点缓存与索引缓存 

 

 

 

顶点缓存与索引缓存之间的关系,宛如俞伯牙与钟子期那种高山流水般的知己情谊,也如千里马与伯乐的那种难得的知遇之恩。

为什么这样说呢,那得看一看在使用顶点缓存配合索引缓存绘制图形时,用到的IDirect3DDevice9::DrawIndexedPrimitive函数了。首先,我们得注意下这个函数的拼写,浅墨发觉经常有朋友会错成DrawIndexPrimitive,注意其中Index后面有个ed,千万不要忘了。正确写法是DrawIndexedPrimitive。这个函数其实和使用顶点缓存绘制图形时用到的IDirect3DDevice9::DrawPrimitive函数用法非常相似,有个别参数基本上一样。我们可以在VS2010的MSDN中查到这个函数有如下原型:

 

HRESULT DrawIndexedPrimitive(
  [in]  D3DPRIMITIVETYPE Type,
  [in]  INT BaseVertexIndex,
  [in]  UINT MinIndex,
  [in]  UINT NumVertices,
  [in]  UINT StartIndex,
  [in]  UINT PrimitiveCount
);

 

█ 第一个参数,D3DPRIMITIVETYPE类型的Type,这个参数和DrawPrimitive方法中第一个参数一摸一样,表示将要绘制的图元类型,在D3DPRIMITIVETYPE枚举体中取值就可以了,其中D3DPRIMITIVETYPE枚举体定义如下:

        

typedef enum D3DPRIMITIVETYPE {
  D3DPT_POINTLIST       = 1,
  D3DPT_LINELIST        = 2,
  D3DPT_LINESTRIP       = 3,
  D3DPT_TRIANGLELIST    = 4,
  D3DPT_TRIANGLESTRIP   = 5,
  D3DPT_TRIANGLEFAN     = 6,
  D3DPT_FORCE_DWORD     = 0x7fffffff 
} D3DPRIMITIVETYPE, *LPD3DPRIMITIVETYPE;

 

其中D3DPT_POINTLIST表示点列,D3DPT_LINELIST表示线列,D3DPT_LINESTRIP表示线带,D3DPT_TRIANGLELIST表示三角形列,D3DPT_TRIANGLESTRIP表示三角形带,D3DPT_TRIANGLEFAN表示三角形扇元,而最后一个D3DPT_FORCE_DWORD不用去理,表示将顶点缓存强制编译为32位,这个参数目前不使用。

 

█ 第二个参数,INT类型的BaseVertexIndex,表示将要进行绘制的索引缓存的起始顶点的索引位置,也就是我们从哪个顶点开始做我们的索引目录,或者说是索引缓存区引用起始位置对应的顶点位置。

█ 第三个参数,UINT类型的MinIndex,表示索引数组中最小的索引值,通常都设为0,这样我们的索引就是0,1,2,3,4,5这样往后排的。

█ 第四个参数,UINT类型的NumVertices,表示我们这次调用DrawIndexedPrimitive方法所需要的顶点个数,也就是为多少个顶点做索引,或者说是索引缓存中使用的顶点数目。

 

 

█ 第五个参数,UINT类型的StartIndex,表示从索引中的第几个索引处开始绘制我们的图元,或者说是索引缓存区开始读索引值的起始位置。

█ 第六个参数,UINT类型的PrimitiveCount,显然就是要绘制的图元个数了。

有些知识点真的是很难用文字表达出来的,正所谓有图有真相。为了便于大家的理解,浅墨依旧是配了一副关于DrawIndexedPrimitive方法,顶点缓存和索引缓存之间的关系图:

 

 

 

在DrawIndexedPrimitive中我们有参数NumVertices指定索引缓存中使用的顶点数目,以及参数StartIndex指定起始索引位置,使我们在使用顶点缓存和索引缓存时更加如鱼得水。比如我们在顶点缓存区保存了多个物体模型的顶点数据,那么就可以在每次调用DrawIndexedPrimitive方法时,通过制定顶点、顶点数和起始索引,可以分别绘制出顶点缓存与索引缓存中存储着的不同物体的模型。

 

这样,我们就可以用一段顶点缓存区配合一段索引缓存区,应付多样化的物体顶点的保存和绘制。

 

通过上面的演绎我们可以发现,索引缓存是最懂顶点缓存的知己了,他可以把顶点缓存的潜力无限地挖掘,让顶点缓存可以用最少的“能量”,迸发出最大的“光亮”来。一如俞伯牙与钟子期那种高山流水般的知己情谊,也如千里马与伯乐的那种难得的知遇之恩。

浅墨在想,如果顶点缓存是个人的话,它一定会发出感叹。

 

知我者,索引缓存也。

 

 

最后,做个总结吧。

索引缓存,就是为了辅佐顶点缓存更好更简洁更高效更有序地绘制图形而存在的。

没有顶点缓存,单单存在索引缓存是没有意义的。

顶点缓存和索引缓存有点相濡以沫的感觉,需要一起使用,双剑合璧,才能迸发出无穷无尽的能量~

 

 

 

 

四、双剑合璧:顶点缓存、索引缓存使用四步曲

 

 

上面我们讲过,索引缓存单独用起来是没有意义的,需要配合顶点缓存一起使用,下面我们就看一起学习,顶点缓存和索引缓

  • 50
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 52
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 52
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值