Easy to understand OpenGL ES 3.0NDK VAO VBO

Catalogue of series articles

Easy to understand OpenGL ES 3.0 (I) essential knowledge for entry!!

Easy to understand OpenGL ES 3.0 (II) rendering triangle

Easy to understand OpenGL ES 3.0 (III) NDK integrated opengl

preface

Next, in the previous article, we will talk about vbo, vao, and the default vertex storage. So explain why you use these things by drawing simple coordinate lines and triangles= v=!!!

design sketch

demo

Draw coordinate lines and triangles

Well, it's like hello world, an entry-level program. So draw two more coordinate lines.

It's similar to the background drawing process in the previous article. In general, it's better to do appropriate things in the rendering callback with the help of the environment provided by GLSurfaceView. It's not so detailed. See the previous article for details.

The header file omits the implementation of triangledemo H (omitted)

TriangleDemo.cpp

//
// Created by Lai on 2020/11/7.
//

#include "TriangleDemo.h"
#include "../util/logUtil.h"
#include "../util/GLUtil.h"
#include <GLES3/gl3.h>


void TriangleDemo::draw(int screenW, int screenH) {
    //LOGCATE("TriangleSample::Draw");

    GLfloat triangleVertices[] = {
            0.0f,  0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
    };
    GLfloat lineVertices[] = {
            -1.0f,  0.0f, 0.0f,
            1.0, -0, 0.0f,
            0.0f,  1.0f, 0.0f,
            0.0f,  -1.0f, 0.0f,
    };

    if(m_ProgramObj == 0)
        return;

    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 1.0, 1.0, 1.0);

    // Use the program object
    glUseProgram (m_ProgramObj);

    // Load the vertex data

    glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, lineVertices );
    glDrawArrays (GL_LINES  , 0, 4);


    glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, triangleVertices );
    glEnableVertexAttribArray (0);

    glDrawArrays (GL_TRIANGLES  , 0, 3);


    glUseProgram (GL_NONE);
}

void TriangleDemo::Init() {

    if (m_ProgramObj != 0) return;

    if(m_ProgramObj != 0)
        return;
    char vShaderStr[] =
            "#version 300 es                          \n"
            "layout(location = 0) in vec4 vPosition;  \n"
            "void main()                              \n"
            "{                                        \n"
            "   gl_Position = vPosition;              \n"
            "gl_PointSize = 50.0; \n"
            "}                                        \n";

    char fShaderStr[] =
            "#version 300 es                              \n"
            "precision mediump float;                     \n"
            "out vec4 fragColor;                          \n"
            "void main()                                  \n"
            "{                                            \n"
            "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
            "}                                            \n";

    LOGCATE("init TriangleDemo");

    m_ProgramObj =  GLUtil::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);
}


void TriangleDemo::OnSurfaceChanged(int width, int height) {
    LOGCATE("MyGLRenderContext::OnSurfaceChanged [w, h] = [%d, %d]", width, height);
    glViewport(0, 0, width, height);
}

Compile the shader, fill the vertex coordinate data with glVertexAttribPointer, and then draw with glDrawArrays

 void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);

Take the vertex coordinates of a line as an example

   GLfloat lineVertices[] = {
            -1.0f,  0.0f, 0.0f,
            1.0, -0, 0.0f,
            0.0f,  1.0f, 0.0f,
            0.0f,  -1.0f, 0.0f,
    };
   //   Draw line / start from 0 / 4 vertices in total
   glDrawArrays (GL_LINES  , 0, 4);

GLenum mode: draw the model. Have the following shapes

GL_APIENTRY glEnableVertexAttribArray (GLuint index);

Turn on vertex position to allow the shader to read GPU data.

The parameter corresponds to the location of the vertex shader, so it is glEnableVertexAttribArray(0);

char vShaderStr[] =
        "#version 300 es                          \n"
        "layout(location = 0) in vec4 vPosition;  \n"  
        "void main()                              \n"
        "{                                        \n"
        "   gl_Position = vPosition;              \n"
        "gl_PointSize = 50.0; \n"
        "}                         

The calling sequence can only be called before the drawing is called.

Then finish the renderings

Usual vertex assignment

glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, lineVertices );

The first parameter 0: corresponds to the position of the vertex shader. For example, the index using the layout(location = 0) Attribute in the vertex shader

The second parameter 3: size, attribute variable data is composed of several elements, x,y,z,w; There are four at most. The vertex data used in this example is three.

Third parameter GL_FLOAT: the type of vertex data. The example here is composed of floating-point values

The fourth parameter, normalized, is usually set to GL_FALSE

The fifth parameter is 0: step size. Write 0 and let opengl decide

The last parameter: without vbo, directly fill in the vertex data

You can see more detailed explanations

In learnopongl Hello, triangle

The api is like this. Let's talk about vertex storage. Now this way is to get data from memory, that is, there are few vertices, so it doesn't matter if you just draw four vertices and make filters. However, if there are more vertex data, you need to consider optimization. Therefore, in order to shorten the distance between cpu and gpu and improve efficiency, this VBO appears.

Vertex buffer objects (VBO)

In general, a large number of vertices are stored in GPU memory (commonly known as video memory) without running around from CPU - > GPU.

The general steps are: generate - > bind - > fill in data

Take the vertex of the triangle to illustrate

Generate VBO cache

//1: the number of vbos to be created,
//&Trianglevbo: store buffer objects
glGenBuffers(1, &triangleVBO);

Binding data

//GL_ARRAY_BUFFER: the type of buffer object. Here is a vertex buffer type
//Triangle VBO: the buffer object that needs to be bound, that is, what is generated above
glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);

Fill in data

//GL_ARRAY_BUFFER: plug point data for the current VBO of the specified type
//sizeof(lineVertices): data size. sizeof calculation
//lineVertices: vertex data
//GL_STATIC_DRAW: GL_ STATIC_ Draw indicates that the data will not or will hardly change.
glBufferData(GL_ARRAY_BUFFER, sizeof(lineVertices), lineVertices, GL_STATIC_DRAW);

Assign values to vertex attributes

//0: layout corresponding to vertex shader (location = 0)
//3: Vertex attributes are more or less a group. For example, mine is x,y,z, so it's 3. The color may be 4 because there are r,g,b,a
//GL_FLOAT: data type
//0: step size
//0: it's 0 directly here. You don't need to fill in vertex data. Go back to the currently bound buffer to find it. Offset of buffer start position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

Then draw

glDrawArrays(GL_TRIANGLES, 0, 3);

Specific code, in order to facilitate understanding, do not get any polymorphic encapsulation. Directly copy and change from the previous one, and directly add the code

Header file omission

TriangleDemoVBO.cpp

//
// Created by Lai on 2020/11/15.
//

#include "TriangleDemoVBO.h"

#include "../util/logUtil.h"
#include "../util/GLUtil.h"
#include <GLES3/gl3.h>


void TriangleDemoVBO::draw(int screenW, int screenH) {
    //LOGCATE("TriangleSample::Draw");

    GLfloat triangleVertices[] = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
    };;
    GLfloat lineVertices[]= {
            -1.0f, 0.0f, 0.0f,
            1.0, -0, 0.0f,
            0.0f, 1.0f, 0.0f,
            0.0f, -1.0f, 0.0f,
    };

    if(m_ProgramObj == 0)
        return;

    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 1.0, 1.0, 1.0);

    // Use the program object
    glUseProgram (m_ProgramObj);

    //Bind object GL_ARRAY_BUFFER: it can be used to save the vertex array data set by glVertexAttribPointer()
    glBindBuffer(GL_ARRAY_BUFFER, lineVBO);
    //GL for the current VBO plug point data_ STATIC_ Draw indicates that the data will not or will hardly change.
    glBufferData(GL_ARRAY_BUFFER, sizeof(lineVertices), lineVertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glDrawArrays(GL_LINES, 0, 4);

    //Bind object GL_ARRAY_BUFFER: it can be used to save the vertex array data set by glVertexAttribPointer()
    glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
    //GL for the current VBO plug point data_ STATIC_ Draw indicates that the data will not or will hardly change.
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glEnableVertexAttribArray(0);

    glUseProgram (GL_NONE);
}

void TriangleDemoVBO::Init() {
    if (m_ProgramObj != 0) return;

    if(m_ProgramObj != 0)
        return;
    char vShaderStr[] =
            "#version 300 es                          \n"
            "layout(location = 0) in vec4 vPosition;  \n"
            "void main()                              \n"
            "{                                        \n"
            "   gl_Position = vPosition;              \n"
            "gl_PointSize = 50.0; \n"
            "}                                        \n";

    char fShaderStr[] =
            "#version 300 es                              \n"
            "precision mediump float;                     \n"
            "out vec4 fragColor;                          \n"
            "void main()                                  \n"
            "{                                            \n"
            "   fragColor = vec4 ( 0.0, 0.0, 0.0, 1);  \n"
            "}                                            \n";

    LOGCATE("init TriangleDemoVBO");

    m_ProgramObj = GLUtil::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);
    //create object
    glGenBuffers(1, &triangleVBO);
    glGenBuffers(1, &lineVBO);

}


void TriangleDemoVBO::OnSurfaceChanged(int width, int height) {
    LOGCATE("MyGLRenderContext::OnSurfaceChanged [w, h] = [%d, %d]", width, height);
    glViewport(0, 0, width, height);
}

I wonder if you have found that glbindbuffer - > glbufferdata - > draw is required for each drawing All need to be bound and filled. It's OK for me to switch between the two models. If there are more vertex data, it's not easy to manage. It will be troublesome to bind to the corresponding VBO, so a thing called VAO comes out to represent whose VBO it is. It's like a class needs a head teacher. It's easy to find it then.

Vertex array object (VAO)

The creation of VAO is similar to that of VBO

//Create VAO
glGenVertexArrays(1, &triangleVAO);

binding

glBindVertexArray(triangleVAO);

Then, like writing VBO at ordinary times, follow the process

//Bind object GL_ARRAY_BUFFER: it can be used to save the vertex array data set by glVertexAttribPointer()
glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
//GL for the current VBO plug point data_ STATIC_ Draw indicates that the data will not or will hardly change.
glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);

In this way, this triangleVBO belongs to this triangleVAO.

So when drawing

glBindVertexArray(triangleVAO);
glDrawArrays(GL_TRIANGLES, 0, 3);

It's that simple.

The specific code is omitted from the header file

//
// Created by Lai on 2020/11/20.
//

#include <logUtil.h>
#include <GLUtil.h>
#include "TriangleDemoVAO.h"


void TriangleDemoVAO::draw(int screenW, int screenH) {
    //LOGCATE("TriangleSample::Draw");
    if(m_ProgramObj == 0)
        return;

    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 1.0, 1.0, 1.0);

    // Use the program object
    glUseProgram (m_ProgramObj);

    glBindVertexArray(triangleVAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);


    glBindVertexArray(lineVAO);
    glDrawArrays(GL_LINES, 0, 4);

    glUseProgram (GL_NONE);
}

void TriangleDemoVAO::Init() {

    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);

    if (m_ProgramObj != 0) return;

    if(m_ProgramObj != 0)
        return;
    char vShaderStr[] =
            "#version 300 es                          \n"
            "layout(location = 0) in vec4 vPosition;  \n"
            "void main()                              \n"
            "{                                        \n"
            "   gl_Position = vPosition;              \n"
            "gl_PointSize = 50.0; \n"
            "}                                        \n";

    char fShaderStr[] =
            "#version 300 es                              \n"
            "precision mediump float;                     \n"
            "out vec4 fragColor;                          \n"
            "void main()                                  \n"
            "{                                            \n"
            "   fragColor = vec4 ( 1.0, 0.5, 0.0, 1);  \n"
            "}                                            \n";

    LOGCATE("init TriangleDemoVAO");

    m_ProgramObj = GLUtil::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);

    GLfloat triangleVertices[] = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
    };
    glGenVertexArrays(1, &triangleVAO);
    //create object
    glGenBuffers(1, &triangleVBO);
    glBindVertexArray(triangleVAO);
    //Bind object GL_ARRAY_BUFFER: it can be used to save the vertex array data set by glVertexAttribPointer()
    glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
    //GL for the current VBO plug point data_ STATIC_ Draw indicates that the data will not or will hardly change.
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);


    GLfloat lineVertices[]= {
            -1.0f, 0.0f, 0.0f,
            1.0, -0, 0.0f,
            0.0f, 1.0f, 0.0f,
            0.0f, -1.0f, 0.0f,
    };

    glGenVertexArrays(1, &lineVAO);
    glGenBuffers(1, &lineVBO);
    glBindVertexArray(lineVAO);
    //Bind object GL_ARRAY_BUFFER: it can be used to save the vertex array data set by glVertexAttribPointer()
    glBindBuffer(GL_ARRAY_BUFFER, lineVBO);
    //GL for the current VBO plug point data_ STATIC_ Draw indicates that the data will not or will hardly change.
    glBufferData(GL_ARRAY_BUFFER, sizeof(lineVertices), lineVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);
}

void TriangleDemoVAO::OnSurfaceChanged(int width, int height) {
    LOGCATE("MyGLRenderContext::OnSurfaceChanged [w, h] = [%d, %d]", width, height);
    glViewport(0, 0, width, height);
}

The generated VAO and VBO are similar, and can be encapsulated into tool classes in the future.

summary

As for whether to use VAO or not, VBO Look at yourself. Many vertex data can be considered. If it's helpful to you, please give me a compliment = v =!!!.

demo

reference resources

Tags: Android OpenGL opengles

Posted by sseeley on Thu, 05 May 2022 09:14:47 +0300