前面我們介紹了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的使用差不多:
- 函數
建立對象IDglGenBuffers
unsigned int EBO;
glGenBuffers(1, &EBO);
- 函數
綁定EBOglBindBuffer
與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);
進行綁定即可使用。
- 在析構函數中調用函數
銷毀EBOglDeleteBuffers
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);
}