OpenGL-ES 学习(7) ---- VBO EBO 和 VAO

目录

      • [VBO(Vertex Buffer Object)](#VBO(Vertex Buffer Object))
      • [EBO(Element Buffer Object)](#EBO(Element Buffer Object))
      • [VAO(Vertex Array Object)](#VAO(Vertex Array Object))

VBO(Vertex Buffer Object)

EBO(Element Buffer Object)

VBO(Vertex Buffer Object) 实际是指顶点缓冲器对象

opengl-es 2.0 的编程中,用于绘制图元的顶点数据是从 CPU 传递到显存中的,具体的方式通过 glVertexAttribPointer 设定顶点或者顶点颜色的值到具体的 index中,OES 在shader 程序中根据 index 获取对应的值,编程方法如下面的示例所示:

c 复制代码
static GLfloat vertices[] = {
        0.0f,  0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
};

static GLfloat verticesColor[] = {
        1.0f,  0.0f, 0.0f,
        0.0f,  1.0f, 0.0f,
        0.0f,  0.0f, 1.0f,
};

....
{
// 指定对应的 vertices 到 index 0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(0);

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, verticesColor);
glEnableVertexAttribArray(1);

// Draw the triangle
glDrawArrays(GL_TRIANGLES, 0, 3);
}

但是如果每次都要绘制相同的图元,就可以把 顶点的值缓存到GPU内存中,不必每次都执行设定加拷贝的过程,这里就可以使用 VBO

不仅顶点的坐标可以缓存到GPU显存中,不同的顶点构成的基本图元索引 也可以进行缓存,这就是 EBO(Element buffer Obejct)

VAO 和 EBO 的作用是在显存中提前开辟好一块内存,用于缓存顶点数据或者图元索引数据,从而避免绘制时的 CPU 和 GPU之间的内存拷贝,可以改进渲染性能,降低内存带宽和功耗

GL_ARRAY_BUFFER 标志指定的缓冲区对象用于保存顶点数组
GL_ELEMENT_ARRAY_BUFFER 标志指定的缓存区对象用于保存图元索引

单独使用 VBO 缓存进行绘制的代码如下(实际也使用了VAO):

c 复制代码
static void DrawPrimitiveWithVBOs(ESContext *esContext)
{
    UserData *userData = esContext->userData;
    GLuint   offset = 0;

    // vboIds[0] - used to store vertex attribute data
    // vboIds[l] - used to store element indices
    if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {
        // Only allocate on the first draw
        glGenBuffers(2, userData->vboIds);
        glGenVertexArrays(1, &userData->vaoId);

        glBindVertexArray(userData->vaoId);

        // 用于缓存顶点的数据
        glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3,GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat) , NULL);
        glEnableVertexAttribArray(0);

        // notice using GL_ARRAY_BUFFER we not using EBO there
        // 用于缓存顶点颜色数据 
        // 顶点和顶点颜色都是使用 VBO 进行缓存
        glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[1]);
        glBufferData(GL_ARRAY_BUFFER,sizeof(verticesColor), verticesColor, GL_STATIC_DRAW);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat) ,NULL);
        glEnableVertexAttribArray(1);

        glBindVertexArray(0);
    }

    glBindVertexArray(userData->vaoId);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glBindVertexArray(0);
}

注意上面使用了 VBO 同时缓存了顶点和顶点颜色数据,但是没有使用 EBO,但是也使用了VAO,根据chatgpt的回答,无法只使用 VBO 不结合 VAO,但是可以不是 VAO

注意 VBO 的使用方法需要先 glBindBuffer 最后 glEnableVertexAttribArray

同时使用 VBOEBO 进行绘制的代码如下:

c 复制代码
unsigned int indices[] = {
        // 前面
        0, 1, 2,
        2, 3, 0,
        // 后面
        4, 5, 6,
        6, 7, 4,
        // 左面
        8, 9, 10,
        10, 11, 8,
        // 右面
        12, 13, 14,
        14, 15, 12,
        // 顶面
        16, 17, 18,
        18, 19, 16,
        // 底面
        20, 21, 22,
        22, 23, 20
};

static void DrawPrimitiveWithVBOs(ESContext *esContext)
{
    UserData *userData = esContext->userData;
    GLuint   offset = 0;

    // vboIds[0] - used to store vertex attribute data
    // vboIds[l] - used to store element indices
    if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {
        // Only allocate on the first draw
        glGenBuffers(2, userData->vboIds);
        glGenVertexArrays(1, &userData->vaoId);
        printf("gen vbo id:%d %d vao id:%d.\n",userData->vboIds[0],userData->vboIds[1],userData->vaoId);

        glBindVertexArray(userData->vaoId);
        
        // 使用 VBO 缓存 顶点和顶点颜色数据,注意这里只使用了一个 VBO, 使用 stride 和 offset 进行区分
        glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3,GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat) , NULL);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat) ,(void*)(3*sizeof(GLfloat)));
        glEnableVertexAttribArray(1);

        // notice using GL_ELEMENT_ARRAY_BUFFER
        // 使用 VBO 缓存顶点的索引数据
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices), indices, GL_STATIC_DRAW);

        glBindVertexArray(0);
    }

    glBindVertexArray(userData->vaoId);
    glUniformMatrix4fv(userData->mvpLoc, 1, GL_FALSE, (GLfloat *)&userData->mvpMatrix.m[0][0]);
    glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_INT,0);
    glBindVertexArray(0);
}

上面的代码中同时使用了VBOEBOVAO进行绘制,注意这里有一个技巧:一个VBOID可以缓存多个数据,可以通过Strideoffset 的方法进行区分

对于每个顶点来说,顶点坐标位于前面,offset0 sizeof(GLFloat)),长度为 3 sizeof(GLFloat)),纹理坐标位于后面, offset3 sizeof(GLFloat)), 长度为 2 sizeof(GLFloat)),每个顶点的步长(stride)就是3+2(sizeof(GLFloat))

VAO(Vertex Array Object)

VAO 是指顶点数组对象,主要用于管理VBO或者EBO,减少 glBindBufferglEnableVertexAttribArrayglVertexAttribPointer 这些调用操作,高效的在顶点属性配置之间切换

使用VAO绘制的示例如下:

c 复制代码
static void DrawPrimitiveWithVBOs(ESContext *esContext)
{
    UserData *userData = esContext->userData;
    GLuint   offset = 0;

    // vboIds[0] - used to store vertex attribute data
    // vboIds[l] - used to store element indices
    if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {
        // Only allocate on the first draw
        glGenBuffers(2, userData->vboIds);
        glGenVertexArrays(1, &userData->vaoId);
        printf("gen vbo id:%d %d vao id:%d.\n",userData->vboIds[0],userData->vboIds[1],userData->vaoId);

        // 开始绑定 vao id
        glBindVertexArray(userData->vaoId);
        
        // 使用 VBO 缓存 顶点和顶点颜色数据,注意这里只使用了一个 VBO, 使用 stride 和 offset 进行区分
        glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3,GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat) , NULL);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat) ,(void*)(3*sizeof(GLfloat)));
        glEnableVertexAttribArray(1);

        // notice using GL_ELEMENT_ARRAY_BUFFER
        // 使用 VBO 缓存顶点的索引数据
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices), indices, GL_STATIC_DRAW);

       // 结束绑定
        glBindVertexArray(0);
    }

    // 开始使用
    glBindVertexArray(userData->vaoId);
    glUniformMatrix4fv(userData->mvpLoc, 1, GL_FALSE, (GLfloat *)&userData->mvpMatrix.m[0][0]);
    glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_INT,0);
    glBindVertexArray(0);
}

主要步骤是:

  • glGenVertexArrays 生产 vao id
  • glBindVertexArray(userData->vaoId); 开始绑定 vao 的操作
  • glBindVertexArray(0); 结束绑定 vao 的操作
相关推荐
✎ ﹏梦醒͜ღ҉繁华落℘28 分钟前
FreeRTOS学习笔记(应用)-- 各种 信号量的应用场景
笔记·学习
星星火柴93633 分钟前
笔记 | C++面向对象高级开发
开发语言·c++·笔记·学习
BeingACoder41 分钟前
【SAA】SpringAI Alibaba学习笔记(一):SSE与WS的区别以及如何注入多个AI模型
java·笔记·学习·saa·springai
安全不再安全2 小时前
免杀技巧 - 早鸟注入详细学习笔记
linux·windows·笔记·学习·测试工具·web安全·网络安全
BreezeJuvenile2 小时前
外设模块学习(8)——HC-SR04超声波模块(STM32)
stm32·单片机·嵌入式硬件·学习·超声波测距模块·hc-sr04
LBuffer2 小时前
破解入门学习笔记题三十八
笔记·学习
微露清风3 小时前
系统性学习C++-第十讲-stack 和 quene
java·c++·学习
PyAIGCMaster3 小时前
钉钉的设计理念方面,我可以学习
人工智能·深度学习·学习·钉钉
Elias不吃糖3 小时前
第四天学习总结:C++ 文件系统 × Linux 自动化 × Makefile 工程化
linux·c++·学习
雍凉明月夜3 小时前
人工智能学习中深度学习之python基础之 类
python·学习