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 的操作
相关推荐
viperrrrrrrrrr73 小时前
大数据学习(105)-Hbase
大数据·学习·hbase
行思理5 小时前
go语言应该如何学习
开发语言·学习·golang
oceanweave6 小时前
【k8s学习之CSI】理解 LVM 存储概念和相关操作
学习·容器·kubernetes
吴梓穆8 小时前
UE5学习笔记 FPS游戏制作43 UI材质
笔记·学习·ue5
学会870上岸华师8 小时前
c语言学习16——内存函数
c语言·开发语言·学习
XYN618 小时前
【嵌入式面试】
笔记·python·单片机·嵌入式硬件·学习
啊哈哈哈哈哈啊哈哈8 小时前
R3打卡——tensorflow实现RNN心脏病预测
人工智能·深度学习·学习
KangkangLoveNLP9 小时前
深度探索:策略学习与神经网络在强化学习中的应用
人工智能·深度学习·神经网络·学习·机器学习·自然语言处理
穷儒公羊10 小时前
第一部分——Docker篇 第六章 容器监控
运维·后端·学习·docker·云原生·容器
CAE虚拟与现实10 小时前
记录一下学习docker的命令(不断补充中)
学习·docker·容器·容器化·docker部署·docker命令