The original article was first published on the wechat official account Byteflow

VBO and EBO

VBO (Vertex Buffer Object) refers to a Vertex Buffer Object, while EBO (Element Buffer Object) refers to a primitive index Buffer Object. VAO and EBO are actually different names for the same class of buffers according to their uses.

In OpenGLES2.0 programming, the data of vertex array used for drawing is stored in CPU memory first. When glDrawArrays or glDrawElements are called for drawing, the data of vertex array needs to be copied from CPU memory to video memory.

However, most of the time we do not need to make a memory copy every time we draw. If we can cache the data in video memory, we can greatly reduce the overhead of memory copy.

OpenGLES3.0 VBO and EBO appear to solve this problem. The function of VBO and EBO is to set up a block of memory in video memory in advance for caching vertex data or pixel index data, thus avoiding memory copy between CPU and GPU during each drawing, improving rendering performance and reducing memory bandwidth and power consumption.

OpenGLES3.0 supports two types of buffer objects: vertex array buffer objects and metaindex buffer objects. The buffer object specified by the GL_ARRAY_BUFFER flag is used to hold the vertex array, and the cache object specified by the GL_ELEMENT_ARRAY_BUFFER flag is used to hold the meta index.

VBO (EBO) creation and update.

// Create two VBos (EBO is the same as VBO, just another name for its purpose)
glGenBuffers(2, m_VboIds);

// Bind the first VBO, copy the vertex array to video memory
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// Bind the second VBO (EBO) to copy the meta index data to video memory
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
Copy the code

The GL_STATIC_DRAW flag indicates that the buffer object data has been modified once, and used multiple times, for drawing.

In this example, the vertex shader and fragment shader add the color attribute:

// Vertex shader
#version 300 es                            
layout(location = 0) in vec4 a_position;   // The position variable's attribute position value is 0
layout(location = 1) in vec3 a_color;      // The attribute position value of the color variable is 1
out vec3 v_color;                          // Outputs a color to the fragment shader
void main(a)                                
{                                          
    v_color = a_color;                     
    gl_Position = a_position;              
};

// Fragment shader
#version 300 es
precision mediump float;
in vec3 v_color;
out vec4 o_fragColor;
void main(a)
{
    o_fragColor = vec4(v_color, 1.0);
}
Copy the code

Vertex array data and meta index data:

// 4 vertices, with(x,y,z) ,(r, g, b, a) per-vertex
GLfloat vertices[] =
		{
				0.5 f.0.5 f.0.0 f.// v0
				1.0 f.0.0 f.0.0 f.// c0
				0.5 f.0.5 f.0.0 f.// v1
				0.0 f.1.0 f.0.0 f.// c1
				0.5 f.0.5 f.0.0 f.// v2
				0.0 f.0.0 f.1.0 f.// c2
				0.5 f.0.5 f.0.0 f.// v3
				0.5 f.1.0 f.1.0 f.// c3
		};
// Index buffer data
GLushort indices[6] = { 0.1.2.0.2.3};
Copy the code

Since the vertex position and color data are in the same array and are updated to the VBO together, we need to know the step size and offset of the two attributes.

To get the next property value in the data queue (such as the next 3-dimensional component of the position vector) we have to move six floats to the right, three of which are position values and three of which are color values, so the step is 6 times the number of bytes of float (= 24 bytes).

Also, you need to specify the VBO memory offset for the vertex position and color attributes. For each vertex, the positional vertex property comes first, so its offset is 0. The color property follows the position data, so the offset is 3 * sizeof(GLfloat), or 12 bytes in bytes.

Draw using VBO and EBO.

glUseProgram(m_ProgramObj);

// Do not use VBO drawing
glEnableVertexAttribArray(0);
glVertexAttribPointer(0.3, GL_FLOAT, GL_FALSE, (3+3) *sizeof(GLfloat), vertices);

glEnableVertexAttribArray(1);
glVertexAttribPointer(1.3, GL_FLOAT, GL_FALSE, (3+3) *sizeof(GLfloat), (vertices + 3));

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);

// Draw using VBO
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0.3, GL_FLOAT, GL_FALSE, (3+3) *sizeof(GLfloat), (const void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1.3, GL_FLOAT, GL_FALSE, (3+3) *sizeof(GLfloat), (const void(*)3 *sizeof(GLfloat)));

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
Copy the code

VAO

Vertex Array Object (VAO) is a Vertex Array Object used to manage a VBO or EBO. Reduce glBindBuffer, glEnableVertexAttribArray, glVertexAttribPointer these calls the operation, to efficiently implement switch between vertex array configuration.

Create a VAO based on the example in the previous section:

// Create and bind VAO
glGenVertexArrays(1, &m_VaoId);
glBindVertexArray(m_VaoId);

// After binding VAO, operate VBO. The current VAO records the operation of VBO
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0.3, GL_FLOAT, GL_FALSE, (3+3) *sizeof(GLfloat), (const void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1.3, GL_FLOAT, GL_FALSE, (3+3) *sizeof(GLfloat), (const void(*)3 *sizeof(GLfloat)));

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);

glBindVertexArray(GL_NONE);
Copy the code

Draw with VAO:

// Is it much leaner?
glUseProgram(m_ProgramObj);

glBindVertexArray(m_VaoId);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
Copy the code

Implementation code path: github.com/githubhaoha…

Contact and exchange