3D游戏编程入门(十五)索引缓存
如我公告所言,7号我将断网进行复习,我很愿意把编程学习看做在学武功,高手都会选择一段时间内进行闭关修炼的,这样更方便自己修身养性,抛开一些网络上的繁杂事宜,很容易全身心的投入自己的事情中,待得出关后功力又会大幅上升,我很渴望技术的进步,特别在弄到的一堆电子书籍之后,我更需要一些时间来好好消化它们,呵呵,想起以前朋友说的话,人与人之间没有永恒的朋友,只有永恒的利益,而书籍永远是人类最忠诚的朋友。虽然这话偏激了些,终究是有一定道理的。对知识的信仰,对力量的渴望,始终是我的追求啊。
呃,废话了,转回正题。
上次我们已经做了个三角形了,但是那仅仅是一个面,大家一定很不过瘾,哪儿象3D了?OK,今天我们来做个立方体,大家就会感觉到很有D3D的成就了,当然我们在三角形之上要加入一些新知识,那就是索引缓存。
索引缓存
我们首先可以来想一下,一个立方体需要几个正方型面?应该是6个,每个正方形面由几个三角形组成?最小应该是两个,那么也就是说,一个正方体最少是需要12个三角形组成,每个三角形需要3个顶点来确定,那么就意味着我们需要给系统36个顶点信息来绘制这个正方体。
现在我们抛弃计算机的任何想法来单纯从立体几何学想下,我们的一个正方体在空间上用几个点可以确定?恩,要没有算错的话应该是8个顶点信息就可以确定正方体了。
36?8?为什么会出现这种情况,理由很容易想到,我们很多的顶点实际上是重复了,我们若还是传36个顶点的话,将会占用更多的顶点缓存,这时我们会想如何有效的利用已有顶点。D3D给我们提供了一个方法来进行顶点的重复使用,就是顶点索引。
顶点索引,就是将我们的所有顶点进行标号索引,之后我们若再使用它的时候,调用它的索引就可以了,无需重新创建一个新的顶点,在的型项目中,这是绝对必须的。一般大型项目中的角色低摸应当是1300左右的三角面,若我们每个三角形给它三个点,则需要3900个点,而我们使用顶点索引的话,仅仅需要1400左右的点,就算顶点结构中仅有三维坐标+颜色+顶点法线+UV纹理+反光材质的信息来说,也省下2500X(8x4+32+8x3+8x2+8x3)=320000字节=312.5K=0.3M多的顶点缓存呢,再加上场景网格的,其他角色模型的。呼,使用顶点索引明显可以节省大量的内存空间,同时,D3D渲染流水线也避免了对相同顶点的重复运算,很大程度上提高了程序的整体性能。
我们知道了为什么必须用索引缓存之后,我们来说下顶点索引的相关概念。我们知道,顶点信息在缓存中存储时必须为它开辟一个空间,之后将它放置到顶点缓冲区中,再进行渲染,但我们在设置顶点缓存时已经为它设置好了相对的缓存区大小,那么我们的索引存放到哪儿呢,当然我们需要为它专门开辟一个索引缓存区(IndexBuffer),它是专门进行存储索引数组的内存缓冲区。这里值得说明的是,在我们索引缓冲时,必须是顺时针来定义三角形的顶点。这点在之后我会再次强调。下面说明下索引缓存的使用步骤。
- 当然,我们首先应该想到,我们设置顶点信息后,应该设置一个数组来记录整个顶点的索引。
// 自定义顶点结构
struct CUSTOMVERTEX
{ FLOAT x, y, z, rhw; // 经过坐标转换的顶点坐标
DWORD color; // 顶点漫反射颜色值
};
// 自定义顶点格式
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
CUSTOMVERTEX g_Vertices[] =
{ { 50.0f , 250.0f, 0.5f, 1.0f, 0xff00ffff, },
{ 50.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, },
{ 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
{ 250.0f, 50.0f, 0.5f, 1.0f, 0xffffffff, },
};
WORD g_Indices[] ={0,1,2,3,2,1};
最下面的g_Indices这个数组就是记录索引的数组,我们可以想想0,1,2三个点绘制的是怎么样的三角形,3,2,1又是如何的一个三角形,我为什么又要设置为3,2,1而不是1,2,3呢?
- 索引数组我们有了,接下来我们建立一个索引缓冲区来存储它,并且将此数组内容放置到开辟的索引缓冲区去。
LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
//创建索引缓冲区
if( FAILED( g_pd3dDevice->CreateIndexBuffer( 6*sizeof(WORD),
0, D3DFMT_INDEX16,
D3DPOOL_DEFAULT, &g_pIB,NULL ) ) )
{
return E_FAIL;
}
VOID* pIndices;
if( FAILED( g_pIB->Lock( 0, sizeof(g_Indices), (void**)&pIndices, 0 ) ) )//锁定索引缓存
return E_FAIL;
memcpy( pIndices, g_Indices, sizeof(g_Indices) );//复制数组内容到索引缓存
g_pIB->Unlock(); //解锁索引缓存区
其中创建索引缓存区的函数声明如下:
HRESULT CreateIndexBuffer(
UINT Length, //索引缓冲区大小,按字节数计算
DWORD Usage, //索引缓冲区属性,和顶点缓冲区相同
D3DFORMAT Format, //索引数组的元素格式,是16位或32位的格式
D3DPOOL Pool, //索引缓冲区内存位置
IDirect3DIndexBuffer9** ppIndexBuffer, //索引缓冲区指针地址
HANDLE* pHandle //保留参数,设为0
)
若看的还不理解,建议您看下上一节我们开辟顶点缓存时的说明,是完全一致的。这里仅仅多了一个Format索引缓存的格式,我们可以填写D3DFMT_INDEX16或D3DFMT_INDEX32,分别代表我们使用16位整数或是32位整数来表示我们的索引值。
- 我们也将设置好的索引数组放置到索引缓存中了,此时我们需要声明我们当前顶点是使用的哪一套索引。在上面的声明中,我们并没有说明这一点,该功能函数的声明如下:
HRESULT SetIndices(
IDirect3DIndexBuffer9 *pIndexData //使用的索引缓冲区指针
);
我们仅仅传一个指针就足够了,这还是相当容易的。
- 好了,告诉了系统我们使用哪套索引了,又设置好了顶点缓存和索引缓存。此时开始绘制了,你会问:上次我们画三角形时不是使用的DrawPrimitive()函数来画了三角形吗?很可惜的是在设置索引后,我们不能再使用DrawPrimitive()函数了,需要使用下面DrawIndexedPrimitive()函数来替代原函数,表示我们正在使用顶点索引的方式来绘制图象。此函数声明如下:
HRESULT DrawIndexedPrimitive(
D3DPRIMITIVETYPE Type, //基本图元的类型
INT BaseVertexIndex, //将要绘制的索引缓冲区的起始地址
UINT MinIndex, //最小的索引数组元素的值
UINT NumVertices, //顶点的数目(从顶点偏移量起始位置到最后一个顶点的总个数,中间的顶点并不一定都绘制)
UINT StartIndex, //开始的索引数组元素的值
UINT PrimitiveCount //绘制的基本图元的数量
);
下面是我以前学习时留下的一些心得和说明,大家可以看下。
//索引顶点的绘制说明
//1.实际绘制使用顶点 = 索引缓存中的索引值 + 顶点数据偏移量
//2.绘制顶点的数量 = 绘制中实际使用的顶点缓存中的顶点数量 - 顶点数据偏移量
//3.当前绘制所使用的索引缓存中的所有索引值都不允许小于使用最小索引值
//4.索引数据偏移量的值不会影响实际的顶点索引值
这样,我们就使用索引缓存绘制了一个正方形,而正方体呢,也仅仅是增加几个顶点信息和索引信息,再改些函数参数咯。
我现在去制作一个去,一会把代码帖上。