在上一篇文章中,我们使用 glDrawArrays(GL_TRIANGLES, 0, 3) 绘制了一个三角形;其中,GL_TRIANGLES 表示我们要绘制的是三角形,那么,我们要怎么绘制矩形呢?把 GL_TRIANGLES 改为 GL_RECTANGLES ?那么绘制五边形,六边形, n 边形呢?显然这样是行不通的。我们先来看下 glDrawArrays 的第一个参数。
OpenGL 支持的图元类型
glDrawArrays 的第一个参数描述的是绘制图元的类型,它有以下几种选项。
- GL_POINTS 点,每个顶点渲染为一个单独的点。
- GL_LINES 线段,每两个顶点构成一条线段。示例:(0, 1)、(2, 3)、(4, 5)...
- GL_LINE_STRIP 折线,相邻顶点构成一条线段。示例:(0, 1)、(1, 2)、(2, 3)...
- GL_LINE_LOOP -封闭折线,相邻顶点构成一条线段,首尾相连。示例:(0, 1)、(1, 2)、(2, 3)...(n - 1, n) (n, 0)
- GL_TRIANGLES - 独立三角形,每三个顶点组成一个三角形。示例:(0, 1, 2)、 (3, 4, 5) ...
- GL_TRIANGLE_STRIP 三角形带,三个顶点组成一个三角形,后面的三角形复用前面两个顶点。示例:(0, 1, 2)、 (1, 2, 3) ...
- GL_TRIANGLE_FAN 三角形扇,相邻两个顶点和第一个顶点组成三角形。示例:(0, 1, 2)、 (0, 2, 3)
原来的 OpenGL 确实是可以画四边形的,但是核心模式废弃了,因为三个点可以确定一个平面,而一个平面上的多边形可以划分成多个三角形。
所以我们要绘制一个矩形,只需要把这个矩形拆成两个三角形即可。
cpp
float vertices[] = {
// 第一个三角形
0.5f, 0.5f, 0.0f, // 右上角
0.5f, -0.5f, 0.0f, // 右下角
-0.5f, 0.5f, 0.0f, // 左上角
// 第二个三角形
0.5f, -0.5f, 0.0f, // 右下角
-0.5f, -0.5f, 0.0f, // 左下角
-0.5f, 0.5f, 0.0f // 左上角
};
然后使用独立三角形模式,绘制 6 个顶点
cpp
glDrawArrays(GL_TRIANGLES, 0, 6);
但是这带来了新的问题,一个矩形只有 4 个顶点,但是我们需要用 6 个顶点去存储它,这会带来额外的开销。我们可以通过 EBO 来优化这个问题。
EBO
元素缓冲对象:Element Buffer Object,EBO 或 索引缓冲对象 Index Buffer Object,IBO,它存储的是顶点的索引。
cpp
float vertices[] = {
0.5f, 0.5f, 0.0f, // 右上角
0.5f, -0.5f, 0.0f, // 右下角
-0.5f, -0.5f, 0.0f, // 左下角
-0.5f, 0.5f, 0.0f // 左上角
};
unsigned int indices[] = {
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
在这个例子中其实并没有很好地体现了这个优化,但在实际 3D 模型中,顶点通常包含:
位置、法线、纹理坐标、切线、副切线等数据,使用索引来绘制比起直接复制顶点数据往往是更优的选择。
VBO 的使用和 EBO 很相似,创建一个 EBO 对象,绑定,赋值。
cpp
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
需要注意的是 EBO 需要在 glEnableVertexAttribArray 之后绑定,在 VAO 解绑之后解绑。

图片源于https://learnopengl-cn.github.io
当调用 glVertexAttribPointer 时,该函数会查询当前绑定到 GL_ARRAY_BUFFER 目标的缓冲对象,将该缓冲对象的 ID 连同其他参数一起存储到当前绑定的 VAO 中,这是一次性的快照操作,之后 VBO 的绑定状态可以改变,不影响已记录的 VAO。GL_ELEMENT_ARRAY_BUFFER 目标的绑定状态直接是 VAO 状态的一部分,当绑定 VAO 后,任何对 GL_ELEMENT_ARRAY_BUFFER 的绑定操作都会直接修改该 VAO 的状态,这是持续的关联,只要 VAO 保持绑定,EBO 就与之关联。
由于笔者水平有限,错误不足之处,烦请各位读者斧正。