有些复杂的图形是由多个相邻的三角形拼接而成的,比如5、6边形等,假如为每个三角形分配3个顶点,但是这样就会出现顶点相互重合的情况,从而造成内存的浪费,显然这是得不偿失的。那有没有更合适的处理方式呢?当然,索引缓冲区可以很好解决这个问题。
索引缓冲区和顶点缓冲区一样,都是COM接口,它其中保存的是多边形的三角形顶点在顶点缓冲区的索引,程序需要做的就是设置三角形的组合,然后通过各种顶点的组合绘制出复杂图形,这样就避免了重复顶点的出现,它的数值最多就是一个16或32位的整数,这要比一个顶点所占用的内存小得多。因此在绘制那些顶点重复使用较多的复杂图形时,用索引缓冲区绘制效果好得多。
下面,我们就利用索引缓冲区绘制一个正多边形(此项目基于上一章内容)。
首先声明索引缓冲区指针对象:
C#
1
LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
然后,修改InitVB方法:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
HRESULT InitVB()
{
//创建顶点缓冲区
VERTEX vertexes[LINE_NUM + 1];
vertexes[0].x = POSITION_X;
vertexes[0].y = POSITION_Y;
vertexes[0].z = 1.0f;
vertexes[0].rhw = 1.0f;
vertexes[0].color = 0xffffffff;
for (int index = 0; index < LINE_NUM; ++index)
{
vertexes[index + 1].x = (float)(RADIUS * sin(index * 2 * PI / LINE_NUM)) + POSITION_X;
vertexes[index + 1].y = -(float)(RADIUS * cos(index * 2 * PI / LINE_NUM)) + POSITION_Y;
vertexes[index + 1].z = 1.0f;
vertexes[index + 1].rhw = 1.0f;
vertexes[index + 1].color = 0xffabcdef;
}
if (FAILED(g_pDevice->CreateVertexBuffer( sizeof(vertexes)
, 0
, FVF
, D3DPOOL_DEFAULT
, &g_pVB
, NULL )))
{
return E_FAIL;
}
void *pvertexes = NULL;
if (FAILED(g_pVB->Lock(0, sizeof(vertexes), (void**)&pvertexes, 0)))
{
return E_FAIL;
}
memcpy(pvertexes, vertexes, sizeof(vertexes));
g_pVB->Unlock();
//创建索引缓冲区
WORD indices[3 * LINE_NUM];
for (int index = 0; index < LINE_NUM; ++index)
{
indices[index * 3] = 0;
indices[(index * 3) + 1] = (index + 1) % (LINE_NUM + 1);
indices[(index * 3) + 2] = (index + 2) % (LINE_NUM + 1) + (index == LINE_NUM - 1 ? 1 : 0);
}
if (FAILED(g_pDevice->CreateIndexBuffer(sizeof(indices),
0,
D3DFMT_INDEX16,
D3DPOOL_DEFAULT,
&g_pIB,
NULL)))
{
return E_FAIL;
}
void *pIndices = NULL;
if (FAILED(g_pIB->Lock(0, sizeof(indices), (void**)&pIndices, 0)))
{
return E_FAIL;
}
memcpy(pIndices, indices, sizeof(indices));
g_pIB->Unlock();
return S_OK;
}
在InitVB方法中,我们修改了原来的顶点缓冲区,因为绘制的是正多边形,所以在填充顶点缓冲区时,使用了三角函数进行处理。初始化索引缓冲区大小时需要注意的是,索引缓冲区中的数据一定要可被3整除,因为我们是以三角形为单位绘制的。
其中CreateIndexBuffer方法创建了索引缓冲区,它有6个参数,第一个参数表示创建的索引缓冲区的大小(以字节为单位);第二个参数表示索引缓冲区的属性,此处取默认值0;第三个参数为索引缓冲区中数据的格式,这个有具体使用的数据为准,此处为16位数据;第四个参数为内存类型(D3DPOOL);第五个参数为索引缓冲区指针地址;第六个参数为保留参数,此处置为NULL。
在填充索引缓冲区时,和填充顶点缓冲区相同,都需要先加锁再解锁,以保证操作的安全。
接着是Render方法,
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void Render()
{
if (NULL == g_pDevice)
{
return;
}
g_pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(30, 60, 90), 1.0f, 0);
if (SUCCEEDED(g_pDevice->BeginScene()))
{
g_pDevice->SetStreamSource(0, g_pVB, 0, sizeof(VERTEX));
g_pDevice->SetFVF(FVF);
g_pDevice->SetIndices(g_pIB);
g_pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, LINE_NUM + 1, 0, LINE_NUM);
g_pDevice->EndScene();
}
g_pDevice->Present(NULL, NULL, NULL, NULL);
}
在Render方法中主要增加了一个SetIndices方法,以及修改原来的绘制方法DrawPrimitive为DrawIndexedPrimitive。SetIndices方法只有一个参数,就是索引缓冲区指针。DrawIndexedPrimitive方法有6个参数,第一个参数是绘制的图元的类型,在此为三角形列表;第二个参数为索引缓冲区的起始地址,此处设置为0,表示从头开始绘制;第三个参数为索引缓冲区中最小的索引值;第四个参数为所要绘制的图形的顶点个数;第五个参数表示从索引缓冲区的哪个元素开始绘制;第六个参数绘制图元的数量。
最后,cleanup方法中添加索引缓冲区对象释放代码,
C#
1
2
3
4
if (NULL != g_pIB)
{
g_pIB->Release();
}
运行程序,我们看到下面的效果,