如我公告所言,7号我将断网进行复习,我很愿意把编程学习看做在学武功,高手都会选择一段时间内进行闭关修炼的,这样更方便自己修身养性,抛开一些网络上的繁杂事宜,很容易全身心的投入自己的事情中,待得出关后功力又会大幅上升,我很渴望技术的进步,特别在弄到的一堆电子书籍之后,我更需要一些时间来好好消化它们,呵呵,想起以前朋友说的话,人与人之间没有永恒的朋友,只有永恒的利益,而书籍永远是人类最忠诚的朋友。虽然这话偏激了些,终究是有一定道理的。对知识的信仰,对力量的渴望,始终是我的追求啊。
呃,废话了,转回正题。
上次我们已经做了个三角形了,但是那仅仅是一个面,大家一定很不过瘾,哪儿象3D了?OK,今天我们来做个立方体,大家就会感觉到很有D3D的成就了,当然我们在三角形之上要加入一些新知识,那就是索引缓存。
呃,废话了,转回正题。
上次我们已经做了个三角形了,但是那仅仅是一个面,大家一定很不过瘾,哪儿象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),它是专门进行存储索引数组的内存缓冲区。这里值得说明的是,在我们索引缓冲时,必须是顺时针来定义三角形的顶点。这点在之后我会再次强调。下面说明下索引缓存的使用步骤。
1:当然,我们首先应该想到,我们设置顶点信息后,应该设置一个数组来记录整个顶点的索引。
// 自定义顶点结构
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呢?
2:索引数组我们有了,接下来我们建立一个索引缓冲区来存储它,并且将此数组内容放置到开辟的索引缓冲区去。
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位整数来表示我们的索引值。
3:我们也将设置好的索引数组放置到索引缓存中了,此时我们需要声明我们当前顶点是使用的哪一套索引。在上面的声明中,我们并没有说明这一点,该功能函数的声明如下:
HRESULT SetIndices(
IDirect3DIndexBuffer9 *pIndexData //使用的索引缓冲区指针
);
我们仅仅传一个指针就足够了,这还是相当容易的。
4:好了,告诉了系统我们使用哪套索引了,又设置好了顶点缓存和索引缓存。此时开始绘制了,你会问:上次我们画三角形时不是使用的DrawPrimitive()函数来画了三角形吗?很可惜的是在设置索引后,我们不能再使用DrawPrimitive()函数了,需要使用下面DrawIndexedPrimitive()函数来替代原函数,表示我们正在使用顶点索引的方式来绘制图象。此函数声明如下:
HRESULT DrawIndexedPrimitive(
现在我们抛弃计算机的任何想法来单纯从立体几何学想下,我们的一个正方体在空间上用几个点可以确定?恩,要没有算错的话应该是8个顶点信息就可以确定正方体了。
36?8?为什么会出现这种情况,理由很容易想到,我们很多的顶点实际上是重复了,我们若还是传36个顶点的话,将会占用更多的顶点缓存,这时我们会想如何有效的利用已有顶点。D3D给我们提供了一个方法来进行顶点的重复使用,就是顶点索引。
顶点索引,就是将我们的所有顶点进行标号索引,之后我们若再使用它的时候,调用它的索引就可以了,无需重新创建一个新的顶点,在的型项目中,这是绝对必须的。一般大型项目中的角色低摸应当是1300左右的三角面,若我们每个三角形给它三个点,则需要3900个点,而我们使用顶点索引的话,仅仅需要1400左右的点,就算顶点结构中仅有三维坐标+颜色+顶点法线+UV纹理+反光材质的信息来说,也省下2500X(8x4+32+8x3+8x2+8x3)=320000字节=312.5K=0.3M多的顶点缓存呢,再加上场景网格的,其他角色模型的。呼,使用顶点索引明显可以节省大量的内存空间,同时,D3D渲染流水线也避免了对相同顶点的重复运算,很大程度上提高了程序的整体性能。
我们知道了为什么必须用索引缓存之后,我们来说下顶点索引的相关概念。我们知道,顶点信息在缓存中存储时必须为它开辟一个空间,之后将它放置到顶点缓冲区中,再进行渲染,但我们在设置顶点缓存时已经为它设置好了相对的缓存区大小,那么我们的索引存放到哪儿呢,当然我们需要为它专门开辟一个索引缓存区(IndexBuffer),它是专门进行存储索引数组的内存缓冲区。这里值得说明的是,在我们索引缓冲时,必须是顺时针来定义三角形的顶点。这点在之后我会再次强调。下面说明下索引缓存的使用步骤。
1:当然,我们首先应该想到,我们设置顶点信息后,应该设置一个数组来记录整个顶点的索引。
// 自定义顶点结构
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呢?
2:索引数组我们有了,接下来我们建立一个索引缓冲区来存储它,并且将此数组内容放置到开辟的索引缓冲区去。
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位整数来表示我们的索引值。
3:我们也将设置好的索引数组放置到索引缓存中了,此时我们需要声明我们当前顶点是使用的哪一套索引。在上面的声明中,我们并没有说明这一点,该功能函数的声明如下:
HRESULT SetIndices(
IDirect3DIndexBuffer9 *pIndexData //使用的索引缓冲区指针
);
我们仅仅传一个指针就足够了,这还是相当容易的。
4:好了,告诉了系统我们使用哪套索引了,又设置好了顶点缓存和索引缓存。此时开始绘制了,你会问:上次我们画三角形时不是使用的DrawPrimitive()函数来画了三角形吗?很可惜的是在设置索引后,我们不能再使用DrawPrimitive()函数了,需要使用下面DrawIndexedPrimitive()函数来替代原函数,表示我们正在使用顶点索引的方式来绘制图象。此函数声明如下:
HRESULT DrawIndexedPrimitive(