1. 程式人生 > >OpenGL緩衝區物件之EBO

OpenGL緩衝區物件之EBO

簡介

EBO(Element Buffer Object,也叫IBO:Index Buffer Object)索引緩衝區物件,這個緩衝區主要用來儲存頂點的索引資訊。
考慮這樣一種情況,我們需要繪製一個立方體
Cube
在指定頂點座標的時候,可以使用以下的一個數組:

GLfloat vertices[] = {
//前
-1,-1,1, //v4
1, -1, 1 //v5
1, 1, 1 //v6
-1, 1, 1 //v7
//左
-1,1,-1 //v0
-1,-1,1 //v4
-1,1,1 //v7
-1,1,-1 //v3
......
};

可以看到事實上整個立方體只有8個頂點,我們儲存頂點座標的數組裡出現了大量重複的頂點。最好的方式應該是每個頂點只需要儲存一次,當我們需要這些頂點時,只需要呼叫頂點的索引來引用的需要的頂點資料

這裡寫圖片描述

EBO就是用來儲存這些索引資料的地方。

使用

1. 建立EBO

建立EBO的方式與建立VBO類似,都是使用glGenBuffers

void glGenBuffers(  GLsizei n,
    GLuint * buffers);
    //n :建立緩衝區數量
    //buffers:存放這些ID的陣列或者變數指標
GLuint eboID;
glGenBuffers(1, &eboID);

2.傳入資料

在建立完成EBO之後,需要向EBO中傳入索引資料,在傳入之前需要繫結將EBO(將eboID設定為當前操作的EBO)

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
; //繫結EBO glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //傳入索引資料到EBO中

3. 繪製幾何體

當設定完成之後,可以呼叫glDrawElements來繪製(如果不使用索引的方式,繪製需要呼叫的函式是glDrawArrays)

void glDrawElements(    
    GLenum mode,    //繪製模式,可以是GL_TRIANGLES、GL_POINTS等     
    GLsizei count, //繪製頂點的次數
    GLenum type,   //索引資料的型別
const GLvoid * indices //EBO中的偏移量(如果不使用EBO,那麼indices指向的是索引陣列的指標) );

示例程式

示例程式中使用EBO的方式繪製一個四邊形

#pragma comment(lib, "glew32.lib")
#pragma comment(lib, "freeglut.lib")


#include <stdio.h>
#include <gl/glew.h>
#include <gl/glut.h>

GLuint eboID;

#define USE_EBO 

GLfloat 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   // 左上角
};

GLuint indices[] = {
    // 起始於0!
    0, 1, 3, // 第一個三角形
    1, 2, 3  // 第二個三角形
};


void ChangeSize(int w, int h)
{
    if (h == 0)
        h = 1;
    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, w*1.0 / h, 0.01, 1000.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void SetupRC()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    glGenBuffers(1, &eboID);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboID);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

}

void RenderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 0.0, 0.0);
    glLoadIdentity();
    glTranslatef(0, 0, -5.0);

    //開啟VA狀態  
    glEnableClientState(GL_VERTEX_ARRAY);

    //繫結資料  
    glVertexPointer(3, GL_FLOAT, 0, vertices);
    //繪製

#ifdef USE_EBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboID);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
#else
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, indices);
#endif
    //關閉VA狀態  
    glDisableClientState(GL_VERTEX_ARRAY);

    glutSwapBuffers();
}


int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("OpenGL");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }

    SetupRC();

    glutMainLoop();
    return 0;
}

為了簡單,沒有使用core profile的方式來使用EBO,程式碼中如果定義USE_EBO,glDrawElements最後一個引數是EBO中的偏移量,否則就是索引陣列的指標。