Learn OpenGLES 3

VAO, VBO and EBO

Source code: https://github.com/chuzcjoe/learnopengles

1. Vertex Buffer Object (VBO)

In our first demo, where we render a triangle in the center of the screen, we define the vertices and store them on the CPU. When glDrawArrays is called, this data is transferred from the CPU to the GPU for rendering. While this method works well for small data sizes, it can become a performance bottleneck if large amounts of data need to be loaded from the CPU to the GPU. VBO is designed to address such issue. VBO manages large batches of data on GPU memory and keep it there if enough space left on GPU. Once the data is in GPU memory, shader programs can have instant access to it.

1
2
3
4
5
6
7
8
9
10
11
12
GLfloat vertices[4 * (3 + 4)] = {
// Vertex positions followed by color attributes
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // v0, c0
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // v1, c1
0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // v2, c2
0.5f, 0.5f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f // v3, c3
};

// Generate VBO and load data
glGenBuffers(1, &mVBO);
glBindBuffer(GL_ARRAY_BUFFER, mVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

2. Vertex Array Object (VAO)

A Vertex Array Object (VAO) in OpenGL is a container object that encapsulates the state needed to specify vertex data to the GPU. It essentially keeps track of multiple vertex buffer objects (VBOs) and their associated vertex attribute configurations, allowing you to easily switch between different vertex data sets and attribute setups.

After a VAO is bound, any subsequent vertex attribute calls will be stored in VAO.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Generate VAO and define how data is stored
glGenVertexArrays(1, &mVAO);
glBindVertexArray(mVAO);

// Generate VBO, EBO and bind them for VAO to capture
// ...

// Specify the layout of the vertex data
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(0));
glEnableVertexAttribArray(0);

glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

// Unbind the VAO (it's a good practice to unbind any VAO to prevent accidental modification)
glBindVertexArray(GL_NONE);

3. Element Buffer Object (EBO)

Element Buffer Objects (EBOs), also known as Index Buffer Objects (IBOs), are an essential feature in OpenGL used for indexing vertex data. They allow you to specify indices that define the order in which vertices are processed, making it possible to reuse vertex data to draw multiple primitives (such as triangles, lines, etc.), which can significantly reduce the amount of memory required and improve performance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// without EBO
float vertices[] = {
// first triangle
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, 0.5f, 0.0f, // top left
// second triangle
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f // top left
};

// with EBO
float vertices[] = {
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f // top left
};
unsigned int indices[] = { // note that we start from 0!
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};

To use EBO:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GLfloat vertices[4 * (3 + 4)] = {
// Vertex positions followed by color attributes
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // v0, c0
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // v1, c1
0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // v2, c2
0.5f, 0.5f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f // v3, c3
};

GLushort indices[6] = {0, 1, 2, 0, 2, 3};

// Generate EBO and load data
glGenBuffers(1, &mEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)(0));

4. Shaders

The vertex shader takes two inputs because we defined two attribute pointers in VAO with location 0(position) and 1(color).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// VAO VBO Shader
const char* VAOVBOVertexShader = VERTEX_SHADER(
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec4 aColor;
out vec4 vColor;
void main() {
gl_Position = vec4(aPosition, 1.0);
vColor = aColor;
}
);

const char* VAOVBOFragmentShader = FRAGMENT_SHADER(
precision mediump float;
in vec4 vColor;
out vec4 FragColor;
void main() {
FragColor = vColor;
}
);

5. Demo

Complete code to setup VAO, VBO and EBO.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
GLfloat vertices[4 * (3 + 4)] = {
// Vertex positions followed by color attributes
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // v0, c0
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // v1, c1
0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // v2, c2
0.5f, 0.5f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f // v3, c3
};

GLushort indices[6] = {0, 1, 2, 0, 2, 3};
LOGD("VAOVBO sample creates shader.");

mShaderProgram = GLUtils::CreateProgram(VAOVBOVertexShader, VAOVBOFragmentShader, mVertexShader, mFragmentShader);

// Generate VAO and define how data is stored
glGenVertexArrays(1, &mVAO);
glBindVertexArray(mVAO);

// Generate VBO and load data
glGenBuffers(1, &mVBO);
glBindBuffer(GL_ARRAY_BUFFER, mVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// Generate EBO and load data
glGenBuffers(1, &mEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// Specify the layout of the vertex data
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(0));
glEnableVertexAttribArray(0);

glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

// Unbind the VAO (it's a good practice to unbind any VAO to prevent accidental modification)
glBindVertexArray(GL_NONE);

Drawing code

1
2
3
4
5
6
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(1.0, 1.0, 1.0, 1.0);
glUseProgram(mShaderProgram);
glBindVertexArray(mVAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)(0));
glBindVertexArray(GL_NONE);

References

Author

Joe Chu

Posted on

2024-07-26

Updated on

2024-07-26

Licensed under

Comments