What OpenGL ES objects do you know that are often asked in interviews?

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 OpenGL ES 2.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.

OpenGL ES 3.0 programming, VBO and EBO is 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.

OpenGL ES 3.0 supports two types of buffer objects: vertex array buffer objects and meta index 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 2 VBos 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), copy the meta index data to video storage glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); The GL_STATIC_DRAW flag indicates that the buffer object data has been modified once, and used multiple times, for drawing.Copy the code

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

#version 300 es layout(location = 0) in vec4 a_position; Layout (location = 1) in vec3 a_color; Vec3 v_color = 1 out vec3 v_color; // Output a color to the fragment shader void main() {v_color = a_color; gl_Position = a_position; }; #version 300 es precision mediump float; in vec3 v_color; out vec4 o_fragColor; Void main() {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); // 4 vertices, with(x,y,z),(r, g, b, a); // 5 vertices, with(x,y,z),(r, g, b, a); / / f c0-0.5, 0.5 f, f, 0.0 0.0 f / / v1, f 1.0, 0.0, f / / c1 0.5 f to 0.5 f, f 0.0, 0.0 f / / v2, 0.0 f, f 1.0, 0.5 f / / c2, 0.5 f, 0.0f, // v3 0.5f, 1.0f, 1.0f, // 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); / / no use VBO draw 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); 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

VAO (Vertex Array Object) is a Vertex Array Object used to manage vBOS or EBOS. 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); 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

UBO

Uniform Buffer Object UBO is a Uniform Buffer Object that holds Uniform variable data. It is essentially the same as other Buffer objects in OpenGL ES, and it is created in roughly the same way.

When data is loaded into UBO, it is stored on UBO instead of being handed to the shader program, so it does not occupy the uniform storage space of the shader program itself. UBO is a new way of transferring data from memory to video memory, and UBO generally needs to be used with UNIFORM blocks.

This example sets the MVP transformation matrix to a Uniform block, meaning that the UBO we will create later will hold three matrices.

#version 310 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;

layout (std140) uniform MVPMatrix
{
    mat4 projection;
    mat4 view;
    mat4 model;
};

out vec2 v_texCoord;
void main()
{
    gl_Position = projection * view * model * a_position;
    v_texCoord = a_texCoord;
}
Copy the code

Set the uniform block binding point to 0 to generate a UBO.

GLuint uniformBlockIndex = glGetUniformBlockIndex(m_ProgramObj, "MVPMatrix"); glUniformBlockBinding(m_ProgramObj, uniformBlockIndex, 0); glGenBuffers(1, &m_UboId); glBindBuffer(GL_UNIFORM_BUFFER, m_UboId); glBufferData(GL_UNIFORM_BUFFER, 3 * sizeof(glm::mat4), nullptr, GL_STATIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); GlBindBufferRange (GL_UNIFORM_BUFFER, 0, m_UboId, 0, 3 * sizeof(GLM ::mat4));Copy the code

Update the Uniform Buffer data when drawing, update the data of the three matrices, pay attention to the offset.

glBindBuffer(GL_UNIFORM_BUFFER, m_UboId);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), &m_ProjectionMatrix[0][0]);
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), &m_ViewMatrix[0][0]);
glBufferSubData(GL_UNIFORM_BUFFER, 2 *sizeof(glm::mat4), sizeof(glm::mat4), &m_ModelMatrix[0][0]);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
Copy the code

FBO

FBO (Frame Buffer Object) is actually a container for adding buffers to which you can add textures or render Buffer objects (RBO).

FBO itself can not be used for rendering, only after adding texture or rendering buffer can be used as a rendering target, it only provides three attachments, respectively color Attachment, depth Attachment and template Attachment.

Render Buffer Object (RBO) is a 2D image Buffer allocated by the application. The render buffer can be used to allocate and store color, depth, or template values and can be used as a color, depth, or template attachment in the FBO.

When using an FBO as a rendering target, you first need to add a connection object for the attachment of the FBO, such as a color buffer that needs to be attached to a texture or render buffer object.

TBO

The Texture Buffer Object (TBO) was introduced in OpenGL ES 3.2, so check the OpenGL ES version first and ensure that the Android API >= 24.

TBO needs to be used in conjunction with Buffer Texture, which is a one-dimensional Texture that stores data from a Texture Buffer object (TBO) to allow shaders to access large memory tables managed by the Buffer object.

In GLSL, only the texelFetch function can be used to access the buffer texture, and the sampler type of the buffer texture is samplerBuffer.

A TBO is generated in the same way as a VBO, just by binding to GL_TEXTURE_BUFFER, and the buffer texture is generated in the same way as a normal 2D texture.

// Generate a Buffer Texture glGenTextures(1, &m_tbotexId); float *bigData = new float[BIG_DATA_SIZE]; for (int i = 0; i < BIG_DATA_SIZE; ++ I) {bigData[I] = I * 1.0f; } // Generate a TBO and upload a large array of buffers to TBO glGenBuffers(1, &m_tBOID); glBindBuffer(GL_TEXTURE_BUFFER, m_TboId); glBufferData(GL_TEXTURE_BUFFER, sizeof(float) * BIG_DATA_SIZE, bigData, GL_STATIC_DRAW); delete [] bigData;Copy the code

To use the fragment shader for the texture buffer, you need to introduce the extension texture Buffer, note that the version is declared as #version 320 ES.

#version 320 es #extension GL_EXT_texture_buffer : require in mediump vec2 v_texCoord; layout(location = 0) out mediump vec4 outColor; uniform mediump samplerBuffer u_buffer_tex; uniform mediump sampler2D u_2d_texture; uniform mediump int u_BufferSize; Void main() {mediump int index = int((v_texcoord.x + v_texcoord.y) /2.0 * float(u_BufferSize - 1)); mediump float value = texelFetch(u_buffer_tex, index).x; Mediump vec4 lightColor = vec4(vec3(value/float(u_buffersize-1)), 0.0), 1.0); outColor = texture(u_2d_texture, v_texCoord) * lightColor; }Copy the code

How to use buffer textures and TBO when drawing?

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, m_TboTexId);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_TboId);
GLUtils::setInt(m_ProgramObj, "u_buffer_tex", 0);

Copy the code

The texture is used in much the same way as a normal texture, except that glTexBuffer is used to bind TBO to the buffer texture.

PBO

Pixel Buffer Object (PBO) is a concept of OpenGL ES 3.0, called Pixel Buffer Object, which is mainly used for asynchronous Pixel transfer operations. PBO is only used to perform pixel transfers, is not connected to textures, and is independent of FBO (frame buffer object).

PBO is similar to VBO (vertex buffer object) in that PBO also opens up GPU cache and stores image data.

PBO can quickly transfer pixel data between gpus’ caches without affecting CPU clock cycles. In addition, PBO supports asynchronous transfer.

PBO is similar to the “space for time” policy. When one PBO is used, the performance cannot be improved effectively, and multiple PBos are usually used alternately.

As shown in the figure above, two PBos are used to read back image data from the frame buffer, and glReadPixels is used to inform GPU to read image data from the frame buffer back to PBO1. Meanwhile, THE CPU can directly process the image data in PBO2.

For more details on the use of PBO, see OpenGL ES 3.0 Development Serial (22) : PBO, which will not be covered here.

Technical communication

Technical exchange/get source code can add my wechat: byte-flow