獲取示例代碼
本文主要講解OpenGL中另一個優(yōu)化技巧付燥,使用頂點索引渲染物體。上一篇文章的例子中渲染正方體需要一個包含36個頂點的數(shù)組愈犹。
本文修改的頂點數(shù)據(jù)都在
Cube.m
中键科。
- (GLfloat *)cubeData {
static GLfloat cubeData[] = {
// X軸0.5處的平面
0.5, -0.5, 0.5f, 1, 0, 0, 0, 0, // VertexA
0.5, -0.5f, -0.5f, 1, 0, 0, 0, 1, // VertexB
0.5, 0.5f, -0.5f, 1, 0, 0, 1, 1, // VertexC
0.5, 0.5, -0.5f, 1, 0, 0, 1, 1, // VertexC
0.5, 0.5f, 0.5f, 1, 0, 0, 1, 0, // VertexD
0.5, -0.5f, 0.5f, 1, 0, 0, 0, 0, // VertexA
// X軸-0.5處的平面
-0.5, -0.5, 0.5f, -1, 0, 0, 0, 0, // VertexE
-0.5, -0.5f, -0.5f, -1, 0, 0, 0, 1, // VertexF
-0.5, 0.5f, -0.5f, -1, 0, 0, 1, 1, // VertexG
-0.5, 0.5, -0.5f, -1, 0, 0, 1, 1, // VertexG
-0.5, 0.5f, 0.5f, -1, 0, 0, 1, 0, // VertexH
-0.5, -0.5f, 0.5f, -1, 0, 0, 0, 0, // VertexE
-0.5, 0.5, 0.5f, 0, 1, 0, 0, 0, // VertexH
-0.5f, 0.5, -0.5f, 0, 1, 0, 0, 1, // VertexG
0.5f, 0.5, -0.5f, 0, 1, 0, 1, 1, // VertexC
0.5, 0.5, -0.5f, 0, 1, 0, 1, 1, // VertexC
0.5f, 0.5, 0.5f, 0, 1, 0, 1, 0, // VertexD
-0.5f, 0.5, 0.5f, 0, 1, 0, 0, 0, // VertexH
-0.5, -0.5, 0.5f, 0, -1, 0, 0, 0, // VertexE
-0.5f, -0.5, -0.5f, 0, -1, 0, 0, 1, // VertexF
0.5f, -0.5, -0.5f, 0, -1, 0, 1, 1, // VertexB
0.5, -0.5, -0.5f, 0, -1, 0, 1, 1, // VertexB
0.5f, -0.5, 0.5f, 0, -1, 0, 1, 0, // VertexA
-0.5f, -0.5, 0.5f, 0, -1, 0, 0, 0, // VertexE
-0.5, 0.5f, 0.5, 0, 0, 1, 0, 0, // VertexH
-0.5f, -0.5f, 0.5, 0, 0, 1, 0, 1, // VertexE
0.5f, -0.5f, 0.5, 0, 0, 1, 1, 1, // VertexA
0.5, -0.5f, 0.5, 0, 0, 1, 1, 1, // VertexA
0.5f, 0.5f, 0.5, 0, 0, 1, 1, 0, // VertexD
-0.5f, 0.5f, 0.5, 0, 0, 1, 0, 0, // VertexH
-0.5, 0.5f, -0.5, 0, 0, -1, 0, 0, // VertexG
-0.5f, -0.5f, -0.5, 0, 0, -1, 0, 1, // VertexF
0.5f, -0.5f, -0.5, 0, 0, -1, 1, 1, // VertexB
0.5, -0.5f, -0.5, 0, 0, -1, 1, 1, // VertexB
0.5f, 0.5f, -0.5, 0, 0, -1, 1, 0, // VertexC
-0.5f, 0.5f, -0.5, 0, 0, -1, 0, 0, // VertexG
};
return cubeData;
}
我在每個頂點后做了注釋,大家可以發(fā)現(xiàn)漩怎,其實一共就8個位置不一樣的頂點勋颖,其他都是重復(fù)的。完全可以替換成下面的表現(xiàn)形式勋锤。cubeVertex
是正方體的8個頂點饭玲,cubeVertexIndice
是cubeData
中的頂點對應(yīng)的索引數(shù)組,使用索引即可在cubeVertex
中取到對應(yīng)的頂點數(shù)據(jù)叁执。這樣一來大大減少了需要傳遞給GPU的數(shù)據(jù)量咱枉。
- (GLfloat *)cubeVertex {
static GLfloat cubeData[] = {
0.5, -0.5, 0.5f, 0.5773502691896258, -0.5773502691896258, 0.5773502691896258, 0, 0, // VertexA
0.5, -0.5f, -0.5f, 0.5773502691896258, -0.5773502691896258, -0.5773502691896258, 0, 1, // VertexB
0.5, 0.5f, -0.5f, 0.5773502691896258, 0.5773502691896258, -0.5773502691896258, 1, 1, // VertexC
0.5, 0.5f, 0.5f, 0.5773502691896258, 0.5773502691896258, 0.5773502691896258, 1, 0, // VertexD
-0.5, -0.5, 0.5f, -0.5773502691896258, -0.5773502691896258, 0.5773502691896258, 0, 0, // VertexE
-0.5, -0.5f, -0.5f, -0.5773502691896258, -0.5773502691896258, -0.5773502691896258, 0, 1, // VertexF
-0.5, 0.5f, -0.5f, -0.5773502691896258, 0.5773502691896258, -0.5773502691896258, 1, 1, // VertexG
-0.5, 0.5f, 0.5f, -0.5773502691896258, 0.5773502691896258, 0.5773502691896258, 1, 0, // VertexH
};
return cubeData;
}
- (GLushort *)cubeVertexIndice {
static GLushort cubeDataIndice[] = {
0, // VertexA
1, // VertexB
2, // VertexC
2, // VertexC
3, // VertexD
0, // VertexA
4, // VertexE
5, // VertexF
6, // VertexG
6, // VertexG
7, // VertexH
4, // VertexE
7, // VertexH
6, // VertexG
2, // VertexC
2, // VertexC
3, // VertexD
7, // VertexH
4, // VertexE
5, // VertexF
1, // VertexB
1, // VertexB
0, // VertexA
4, // VertexE
7, // VertexH
4, // VertexE
0, // VertexA
0, // VertexA
3, // VertexD
7, // VertexH
6, // VertexG
5, // VertexF
1, // VertexB
1, // VertexB
2, // VertexC
6, // VertexG
};
return cubeDataIndice;
}
那么,現(xiàn)在問題來了徒恋,怎么渲染這種形式的數(shù)據(jù)呢蚕断?這就是下面要介紹的內(nèi)容。
創(chuàng)建索引數(shù)組IBO
在前文中我們?yōu)轫旤c數(shù)據(jù)創(chuàng)建了VBO入挣,同樣我們也可以為索引數(shù)據(jù)創(chuàng)建IBO(Index Buffer Object)亿乳。
- (void)genIndiceVBO {
glGenBuffers(1, &indiceVbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceVbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 36 * sizeof(GLushort), [self cubeVertexIndice], GL_STATIC_DRAW);
}
和創(chuàng)建VBO唯一不同的就是GL_ELEMENT_ARRAY_BUFFER
,創(chuàng)建VBO是這里的值是GL_ARRAY_BUFFER
径筏。然后我們在生成VAO的地方增加綁定IBO的代碼葛假。
- (void)genVAO {
glGenVertexArraysOES(1, &vao);
glBindVertexArrayOES(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceVbo);
[self.context bindAttribs:NULL];
glBindVertexArrayOES(0);
}
這里的vbo
綁定的數(shù)據(jù)已經(jīng)修改為cubeVertex
里的數(shù)據(jù)了。
- (void)genVBO {
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 8 * 8 * sizeof(GLfloat), [self cubeVertex], GL_STATIC_DRAW);
}
最后就是繪制部分的修改了滋恬,我在GLContext
中增加了一個方法聊训。
- (void)drawTrianglesWithIndicedVAO:(GLuint)vao vertexCount:(GLint)vertexCount {
glBindVertexArrayOES(vao);
glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, (void *)0);
}
主要就是使用了glDrawElements
繪制帶索引的頂點數(shù)組。其中GL_UNSIGNED_SHORT
指的是索引數(shù)組中每個元素的類型恢氯。這里索引數(shù)組用的是static GLushort cubeDataIndice[]
带斑,所以使用GL_UNSIGNED_SHORT
。除了這個值外勋拟,還能用的值為GL_UNSIGNED_BYTE
勋磕,GL_UNSIGNED_INT
。下面是glDrawElements
的man文檔敢靡。
到此就可以渲染出正方體了挂滓。
看到這奇怪的渲染效果,很明顯啸胧,事情還沒有結(jié)束赶站。如果你有看基本光照這一篇文章幔虏,應(yīng)該會知道法線可以有下面兩種表現(xiàn)方式。
前者是每個三角形的每個頂點都有一個法線數(shù)據(jù)贝椿,對應(yīng)不使用索引時的情況所计。后者是每個頂點有一個法線數(shù)據(jù),共享的點的法線是這個點在各個三角形上的法線之和歸一化后的結(jié)果团秽。
- (GLfloat *)cubeVertex {
static GLfloat cubeData[] = {
0.5, -0.5, 0.5f, 0.5773502691896258, -0.5773502691896258, 0.5773502691896258, 0, 0, // VertexA
0.5, -0.5f, -0.5f, 0.5773502691896258, -0.5773502691896258, -0.5773502691896258, 0, 1, // VertexB
0.5, 0.5f, -0.5f, 0.5773502691896258, 0.5773502691896258, -0.5773502691896258, 1, 1, // VertexC
0.5, 0.5f, 0.5f, 0.5773502691896258, 0.5773502691896258, 0.5773502691896258, 1, 0, // VertexD
-0.5, -0.5, 0.5f, -0.5773502691896258, -0.5773502691896258, 0.5773502691896258, 0, 0, // VertexE
-0.5, -0.5f, -0.5f, -0.5773502691896258, -0.5773502691896258, -0.5773502691896258, 0, 1, // VertexF
-0.5, 0.5f, -0.5f, -0.5773502691896258, 0.5773502691896258, -0.5773502691896258, 1, 1, // VertexG
-0.5, 0.5f, 0.5f, -0.5773502691896258, 0.5773502691896258, 0.5773502691896258, 1, 0, // VertexH
};
return cubeData;
}
cubeVertex
里面的法線數(shù)據(jù)就是我手動計算的結(jié)果主胧。這樣產(chǎn)生的法線會讓表面過渡平滑。除了法線习勤,UV也會面臨同樣的問題踪栋,所以渲染出來的貼圖才會不正確。
如果為每個頂點屬性指定不同的VBO和IBO是可以解決這個問題的图毕,這個方案將會在后面介紹加載wavefront 3D模型時詳細(xì)介紹夷都。