前面我们介绍了VBO与VAO等缓冲对象,今天我们来介绍一下EBO。
对于VBO或VAO可以查看之前的文章:Opengl ES之VBO和VAO
EBO是个啥
EBO(Element Buffer Object,也叫IBO:Index Buffer Object)索引缓冲区对象,这个缓冲区主要用来存储顶点的索引信息,索引的意义在于减少重复数据,主要是在函数glDrawElements
中使用。
在之前的文章中,我们绘制三角形、四边形等,因为顶点数据或者公用的顶点坐标不多,都是调用函数glDrawArrays
进行绘制的,试想一下,如果需要绘制一个顶点数量比较多,而且多个顶点之间需要经过
不同的组合进行顶点复用的,那么函数glDrawArrays
及很难满足绘制需求了,此时就需要使用EBO搭配函数glDrawElements
进行绘制了,例如使用8个顶点绘制一个正方体的例子。
在OpenGL通过索引缓冲对象(EBO)来对顶点进行复用,做到重复的顶点只需要分配一次内存,再绘图的时候后通过EBO告诉GPU顶点的索引。
如果使用了EBO,那么绘图函数glDrawElements
通过索引到相应的顶点缓冲区去拿数据,如果绑定了VAO就到VAO里拿数据。
EBO的使用
EBO的使用和前面介绍的VBO的使用差不多:
- 函数
glGenBuffers
创建对象ID
unsigned int EBO;
glGenBuffers(1, &EBO);
- 函数
glBindBuffer
绑定EBO
与VBO类似,只不过EBO的类型变成了GL_ELEMENT_ARRAY_BUFFER
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
- 函数
glBufferData
绑定数据
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
对于GL_STATIC_DRAW等几个参数的类型在VBO的文章中已经做了介绍,这里就不多说了,童鞋们可以看之前的文章。
当以上步骤配置完毕就可以使用EBO了,在绘制函数glDrawElements
调用前调用函数glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
进行绑定即可使用。
- 在析构函数中调用函数
glDeleteBuffers
销毁EBO
glDeleteBuffers(1,&EBO);
注意:笔者在结合使用VAO、VBO和EBO的时候发现,解除绑定时需要先解除VAO的绑定,再解除其他VBO及EBO的绑定,否则可能会导致绘制不生效。
实例Demo
我们沿用之前VBO/VAO绘制四边形的例子,在上面拓展一下使用四个顶点坐标结合EBO绘制一个四边形。
主要代码EBOOpengl.cpp:
#include "EBOOpengl.h"
#include "../utils/Log.h"
// 顶点着色器
static const char *ver = "#version 300 es\n"
"in vec4 aColor;\n"
"in vec4 aPosition;\n"
"out vec4 vColor;\n"
"void main() {\n"
" vColor = aColor;\n"
" gl_Position = aPosition;\n"
"}";
// 片元着色器
static const char *fragment = "#version 300 es\n"
"precision mediump float;\n"
"in vec4 vColor;\n"
"out vec4 fragColor;\n"
"void main() {\n"
" fragColor = vColor;\n"
"}";
const static GLfloat VERTICES_AND_COLOR[] = {
0.5f, -0.5f, // 右下
// 颜色
0.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, // 右上
// 颜色
0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, -0.5f, // 左下
// 颜色
0.0f, 0.0f, 1.0f, 1.0f,
-0.5f, 0.5f, // 左上
// 颜色
0.0f, 0.0f, 1.0f, 1.0f,
};
//const static unsigned int indices[] = {
// // 注意索引从0开始!
// // 此例的索引(0,1,2,3)就是顶点数组vertices的下标,
// // 这样可以由下标代表顶点组合成矩形
// 0, 1, 2, // 第一个三角形
// 1, 2, 3 // 第二个三角形
//};
//// 使用shor类型
//const static unsigned short indices[] = {
// // 注意索引从0开始!
// // 此例的索引(0,1,2,3)就是顶点数组vertices的下标,
// // 这样可以由下标代表顶点组合成矩形
// 0, 1, 2, // 第一个三角形
// 1, 2, 3 // 第二个三角形
//};
// 使用byte类型
const static uint8_t indices[] = {
// 注意索引从0开始!
// 此例的索引(0,1,2,3)就是顶点数组vertices的下标,
// 这样可以由下标代表顶点组合成矩形
0, 1, 2, // 第一个三角形
1, 2, 3 // 第二个三角形
};
EBOOpengl::EBOOpengl() {
initGlProgram(ver, fragment);
positionHandle = glGetAttribLocation(program, "aPosition");
colorHandle = glGetAttribLocation(program, "aColor");
// VAO
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// vbo
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES_AND_COLOR), VERTICES_AND_COLOR, GL_STATIC_DRAW);
// stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float
glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *) 0);
// 启用顶点数据
glEnableVertexAttribArray(positionHandle);
// stride 步长 每个颜色坐标之间相隔6个数据点,数据类型是float,颜色坐标索引从2开始
glVertexAttribPointer(colorHandle, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
(void *) (2 * sizeof(float)));
// 启用颜色顶点数据
glEnableVertexAttribArray(colorHandle);
// EBO
glGenBuffers(1,&ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);
// 这个顺序不能乱啊,先解除vao,再解除其他的,不然在绘制的时候可能会不起作用,需要重新glBindBuffer才生效
// vao解除
glBindVertexArray(0);
// 解除绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 解除绑定
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
LOGD("program:%d", program);
LOGD("positionHandle:%d", positionHandle);
LOGD("colorHandle:%d", colorHandle);
}
void EBOOpengl::onDraw() {
// 清屏
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
// VBO与VAO配合绘制
// 使用vao
glBindVertexArray(vao);
// 使用EBO
// glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,(void *)0);
// 使用short类型节省内存
// glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_SHORT,(void *)0);
// 使用byte类型节省内存
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_BYTE,(void *)0);
glUseProgram(0);
// vao解除绑定
glBindVertexArray(0);
// 禁用顶点
glDisableVertexAttribArray(positionHandle);
if (nullptr != eglHelper) {
eglHelper->swapBuffers();
}
}
EBOOpengl::~EBOOpengl() noexcept {
glDeleteBuffers(1,&ebo);
glDeleteBuffers(1,&vbo);
glDeleteVertexArrays(1,&vao);
}
技巧:在确保数据不会发生溢出的情况下尽量使用占有内存小的数据类型以节约内存,例如使用short比int要节省,使用byte比short要节省。
往期笔记
Opengl ES之EGL环境搭建
Opengl ES之着色器
Opengl ES之三角形绘制
Opengl ES之四边形绘制
Opengl ES之纹理贴图
Opengl ES之VBO和VAO
关注我,一起进步,人生不止coding!!!