前言

本文次要介绍了什么是VBO/VAO,为什么须要应用VBO/VAO以及如何应用VBO和VAO。

VBO

什么是VBO

VBO(vertex Buffer Object):顶点缓冲对象。是在显卡存储空间中开拓的一块区域,在显卡存储空间中开拓一块区域,用于寄存顶点的各类属性信息。如顶点坐标、纹理坐标、顶点色彩等数据。
在渲染时间接从显VBO去取数据而不用与CPU进行数据交换。

为什么须要应用VBO

将顶点数据保留在内存中,在调用glDrawArrays或者glDrawElements等绘制办法前须要调用相应的办法将数据送入显存,I/O开销大,性能不够好。
若采纳顶点缓冲区对象寄存顶点数据,则不须要在每次绘制前都将顶点数据复制进显存,而是在初始化顶点缓冲区对象时一次性将顶点数据送入显存,
每次绘制时间接应用显存中的数据,能够大大提高渲染性能。

如何应用VBO

  • 应用函数glGenBuffers和一个缓冲ID生成一个VBO对象:

    unsigned int VBO;glGenBuffers(1, &VBO);
  • 应用函数glBindBuffer绑定顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER

OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。

glBindBuffer(GL_ARRAY_BUFFER, VBO); 
  • 应用函数glBufferData把定义好的顶点数据复制到缓冲的内存中:

    // vertices示意顶点数组glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBufferData是一个专门用来把用户定义的数据复制到以后绑定缓冲的函数。它的第一个参数是指标缓冲的类型,其中VBO代表是的GL_ARRAY_BUFFER。第二个参数指定传输数据的大小(以字节为单位),用一个简略的sizeof计算出顶点数据大小就行。
第三个参数是咱们心愿发送的理论数据。第四个参数指定了咱们心愿显卡如何治理给定的数据,它有三种模式:

GL_STATIC_DRAW :数据不会或简直不会扭转。
GL_DYNAMIC_DRAW:数据会被扭转很多。
GL_STREAM_DRAW :数据每次绘制时都会扭转。

个别状况下地位数据不会扭转,每次渲染调用时都放弃原样,所以它的应用类型个别是GL_STATIC_DRAW。如果一个缓冲中的数据将频繁被扭转,那么应用的类型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW,这样就能确保显卡把数据放在可能高速写入的内存局部。

  • 用完后应用函数glDeleteBuffers删除缓冲区

明天咱们以之前绘制四边形的实际为例子,应用VBO的形式来实现四边形的绘制:Opengl ES之四边形绘制

咱们的指标是灵便应用Opengl绘制一个蓝色的四边形...

一般惯例的绘制这里就不多说了,后续能够看代码,或者回顾之前的四边形绘制的文章,这里次要介绍搭配VBO的两种绘制形式:

首先它们应用的顶点着色器和片段着色器都是一样的,都是:

// 顶点着色器static const char *ver = "#version 300 es\n"                         "in vec4 aColor;\n"                         "in vec4 aPosition;\n"                         "out vec4 vColor;\n"                         "void main() {\n"                         "    vColor = aColor;\n"                         "    gl_Position = aPosition;\n"                         "}";// 片元着色器static const char *fragment = "#version 300 es\n"                              "precision mediump float;\n"                              "in vec4 vColor;\n"                              "out vec4 fragColor;\n"                              "void main() {\n"                              "    fragColor = vColor;\n"                              "}";
  1. 顶点坐标与色彩值坐标拆散的形式(数组构造)

先看顶点数据与色彩数据:

// 应用绘制两个三角形组成一个矩形的模式(三角形带)// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形const static GLfloat VERTICES[] = {        0.5f,-0.5f, // 右下        0.5f,0.5f, // 右上        -0.5f,-0.5f, // 左下        -0.5f,0.5f, // 左上};// vbo色彩const static GLfloat COLOR_ICES[] = {        0.0f,0.0f,1.0f,1.0f,        0.0f,0.0f,1.0f,1.0f,        0.0f,0.0f,1.0f,1.0f,        0.0f,0.0f,1.0f,1.0f,};

VBO数据与绑定:

  // vbo    glGenBuffers(3,vbo);    glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);    glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES),VERTICES,GL_STATIC_DRAW);    // 色彩    glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);    glBufferData(GL_ARRAY_BUFFER,sizeof(COLOR_ICES),COLOR_ICES,GL_STATIC_DRAW);

次要绘制代码:

     // 应用VBO的形式绘制,顶点与色彩离开//    glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);//    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,(void *)0);//    // 启用顶点数据//    glEnableVertexAttribArray(positionHandle);////    glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);//    glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,0,(void *)0);//    // 启用色彩顶点数据//    glEnableVertexAttribArray(colorHandle);//    // 解除绑定//    glBindBuffer(GL_ARRAY_BUFFER,0);
  1. 顶点坐标与色彩值坐标联合的形式(构造数组)

顶点数据与色彩数据混合打乱:

const static GLfloat VERTICES_AND_COLOR[] = {        0.5f,-0.5f, // 右下        // 色彩        0.0f,0.0f,1.0f,1.0f,        0.5f,0.5f, // 右上        // 色彩        0.0f,0.0f,1.0f,1.0f,        -0.5f,-0.5f, // 左下        // 色彩        0.0f,0.0f,1.0f,1.0f,        -0.5f,0.5f, // 左上        // 色彩        0.0f,0.0f,1.0f,1.0f,};

VBO数据绑定:

    // vbo    glGenBuffers(3,vbo);    glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);    glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES),VERTICES,GL_STATIC_DRAW);    // 色彩    glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);    glBufferData(GL_ARRAY_BUFFER,sizeof(COLOR_ICES),COLOR_ICES,GL_STATIC_DRAW);    // 顶点与颜色混合,先顶点坐标,再色彩坐标    glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);    glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES_AND_COLOR),VERTICES_AND_COLOR                 ,GL_STATIC_DRAW);

次要绘制代码:

    // VBO绘制 顶点坐标与色彩坐标一起//    glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);//    // stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float//    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,6 * sizeof(float ),(void *)0);//    // 启用顶点数据//    glEnableVertexAttribArray(positionHandle);//    // stride 步长 每个色彩坐标之间相隔6个数据点,数据类型是float,色彩坐标索引从2开始//    glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,6 * sizeof(float ) ,(void *)(2 * sizeof(float)) );//    // 启用色彩顶点数据//    glEnableVertexAttribArray(colorHandle);//    // 解除绑定//    glBindBuffer(GL_ARRAY_BUFFER,0);

它们的运行后果都是胜利绘制了一个蓝色的四边形:

性能

在下面咱们应用了多种不同的组合形式进行了四边形的绘制,其中有将顶点坐标和色彩坐标拆散写在两个不同的数组的形式,也有将顶点坐标和色彩坐标组合写在同一个数组,而后应用步长(stride)和偏移量(*pointer)参数管制的形式进行绘制,那么这两种形式那种性能更佳呢?

其中将各种顶点坐标拆散在不同数组的写法又成为数组构造,而将各种顶点坐标合并成一个数组的写法又称为构造数组,在《OPENGL ES 3.0编程指南》一书中作者指出,构造数组的写法性能更好。

在大部分状况下,答案是构造数组。
起因是,每个顶点的属性数据能够程序形式读取,这最有可能造成高效的内存拜访模式。

VAO

什么是VAO

VAO(vertex Array Object):顶点数组对象。

留神: VAO是OpenGL ES 3.0之后才推出的新个性, 所以在应用VAO前须要确定OpenGL ES的版本是否是3.0之后的版本。

顶点数组对象能够像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会贮存在这个VAO中。这样的益处就是,当配置顶点属性指针时,你只须要将那些调用执行一次,之后再绘制物体的时候只须要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只须要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中。

一个顶点数组对象会贮存以下这些内容:

  • glEnableVertexAttribArray和glDisableVertexAttribArray的调用。
  • 通过glVertexAttribPointer设置的顶点属性配置。
  • 通过glVertexAttribPointer调用与顶点属性关联的顶点缓冲对象。

在下面VBO的介绍中咱们晓得每次在绘制的时候都须要频繁地绑定与解绑VBO,每次绘制还须要取出VBO中的数据进行赋值之后能力进行绘制渲染。当数据量大的时候,反复这些操作就会变得很繁琐。通过VAO就能够简化这个过程,因而VAO能够简略了解成VBO的管理者,防止在帧绘制时再去手动操纵VBO,VAO不能独自应用,
须要搭配VBO应用。

对于GPU来说VBO就是一堆数据,然而这堆数据怎么解析应用,须要glEnableVertexAttribArray等相干函数在每次绘制的时候通知GPU,那么VAO的作用就是简化这个过程的,只须要在初始化的时候将这些解析逻辑与VAO绑定一次即可,
而后每次在绘制的时候只需绑定对应的VAO,而不用每次再绑定VBO,而后去通知GPU如何解析相干数据了,能够说是一个性能的优化了。

如何应用VAO

  • 首先调用函数glGenVertexArrays生成VAO
unsigned int VAO;glGenVertexArrays(1, &VAO);
  • 调用函数glBindVertexArray绑定VAO
glBindVertexArray(VAO);// 治理VBO,让VAO记住VBO的数据如何解析应用glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 3. 设置顶点属性指针glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);glBindVertexArray(0);

而后在绘制的时候应用函数glBindVertexArray(VAO)应用即可。

  • 退出时通过函数glDeleteVertexArrays删除VAO
glDeleteVertexArrays

次要绑定代码:

    // VAO    glGenVertexArrays(1,&vao);    glBindVertexArray(vao);    // VAO与VBO关联    glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);    // stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,6 * sizeof(float ),(void *)0);    // 启用顶点数据    glEnableVertexAttribArray(positionHandle);    // stride 步长 每个色彩坐标之间相隔6个数据点,数据类型是float,色彩坐标索引从2开始    glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,6 * sizeof(float ) ,(void *)(2 * sizeof(float)) );    // 启用色彩顶点数据    glEnableVertexAttribArray(colorHandle);    // 解除绑定    glBindBuffer(GL_ARRAY_BUFFER,0);    glBindVertexArray(0);

次要应用绘制代码:

   // VBO与VAO配合绘制    // 应用vao    glBindVertexArray(vao);    // 4个顶点绘制两个三角形组成矩形    glDrawArrays(GL_TRIANGLE_STRIP,0,4);    glUseProgram(0);    // vao解除绑定    glBindVertexArray(vao);

上面是残缺代码:

VBOVAOOpengl.h

static const int NUM_VBO = 3;class VBOVAOOpengl:public BaseOpengl{public:    VBOVAOOpengl();    virtual ~VBOVAOOpengl();    virtual void onDraw();private:    GLint positionHandle{-1};    GLint colorHandle{-1};    GLuint vbo[NUM_VBO];    GLuint vao{0};};

VBOVAOOpengl.cpp

// 顶点着色器static const char *ver = "#version 300 es\n"                         "in vec4 aColor;\n"                         "in vec4 aPosition;\n"                         "out vec4 vColor;\n"                         "void main() {\n"                         "    vColor = aColor;\n"                         "    gl_Position = aPosition;\n"                         "}";// 片元着色器static const char *fragment = "#version 300 es\n"                              "precision mediump float;\n"                              "in vec4 vColor;\n"                              "out vec4 fragColor;\n"                              "void main() {\n"                              "    fragColor = vColor;\n"                              "}";// 应用绘制两个三角形组成一个矩形的模式(三角形带)// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形const static GLfloat VERTICES[] = {        0.5f,-0.5f, // 右下        0.5f,0.5f, // 右上        -0.5f,-0.5f, // 左下        -0.5f,0.5f, // 左上};const static GLfloat VERTICES_AND_COLOR[] = {        0.5f,-0.5f, // 右下        // 色彩        0.0f,0.0f,1.0f,1.0f,        0.5f,0.5f, // 右上        // 色彩        0.0f,0.0f,1.0f,1.0f,        -0.5f,-0.5f, // 左下        // 色彩        0.0f,0.0f,1.0f,1.0f,        -0.5f,0.5f, // 左上        // 色彩        0.0f,0.0f,1.0f,1.0f,};// rgba//const static GLfloat COLOR_ICES[] = {//        0.0f,0.0f,1.0f,1.0f//};// vbo色彩const static GLfloat COLOR_ICES[] = {        0.0f,0.0f,1.0f,1.0f,        0.0f,0.0f,1.0f,1.0f,        0.0f,0.0f,1.0f,1.0f,        0.0f,0.0f,1.0f,1.0f,};VBOVAOOpengl::VBOVAOOpengl() {    initGlProgram(ver,fragment);    positionHandle = glGetAttribLocation(program,"aPosition");    colorHandle = glGetAttribLocation(program,"aColor");    // vbo    glGenBuffers(3,vbo);    glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);    glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES),VERTICES,GL_STATIC_DRAW);    // 色彩    glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);    glBufferData(GL_ARRAY_BUFFER,sizeof(COLOR_ICES),COLOR_ICES,GL_STATIC_DRAW);    // 顶点与颜色混合,先顶点坐标,再色彩坐标    glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);    glBufferData(GL_ARRAY_BUFFER,sizeof(VERTICES_AND_COLOR),VERTICES_AND_COLOR                 ,GL_STATIC_DRAW);    glBindBuffer(GL_ARRAY_BUFFER,0);    // VAO    glGenVertexArrays(1,&vao);    glBindVertexArray(vao);    // VAO与VBO关联    glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);    // stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,6 * sizeof(float ),(void *)0);    // 启用顶点数据    glEnableVertexAttribArray(positionHandle);    // stride 步长 每个色彩坐标之间相隔6个数据点,数据类型是float,色彩坐标索引从2开始    glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,6 * sizeof(float ) ,(void *)(2 * sizeof(float)) );    // 启用色彩顶点数据    glEnableVertexAttribArray(colorHandle);    // 解除绑定    glBindBuffer(GL_ARRAY_BUFFER,0);    glBindVertexArray(0);    LOGD("program:%d",program);    LOGD("positionHandle:%d",positionHandle);    LOGD("colorHandle:%d",colorHandle);}void VBOVAOOpengl::onDraw() {    // 清屏    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);    glClear(GL_COLOR_BUFFER_BIT);    glUseProgram(program);    // 一般形式绘制//    /**//     * size 几个数字示意一个点,显示是两个数字示意一个点//     * normalized 是否须要归一化,不必,这里曾经归一化了//     * stride 步长,间断顶点之间的距离,如果顶点间接是间断的,也可填0//     *///    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);//    // 启用顶点数据//    glEnableVertexAttribArray(positionHandle);////    // 这个不须要glEnableVertexAttribArray//    glVertexAttrib4fv(colorHandle, COLOR_ICES);// ############################################# 分割线 #################################     // 应用VBO的形式绘制,顶点与色彩离开    //    /**//    glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);//    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,(void *)0);//    // 启用顶点数据//    glEnableVertexAttribArray(positionHandle);////    glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);//    glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,0,(void *)0);//    // 启用色彩顶点数据//    glEnableVertexAttribArray(colorHandle);//    // 解除绑定//    glBindBuffer(GL_ARRAY_BUFFER,0);// ############################################# 分割线 #################################    // VBO绘制 顶点坐标与色彩坐标一起//    glBindBuffer(GL_ARRAY_BUFFER,vbo[2]);//    // stride 步长 每个顶点坐标之间相隔6个数据点,数据类型是float//    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,6 * sizeof(float ),(void *)0);//    // 启用顶点数据//    glEnableVertexAttribArray(positionHandle);//    // stride 步长 每个色彩坐标之间相隔6个数据点,数据类型是float,色彩坐标索引从2开始//    glVertexAttribPointer(colorHandle,4,GL_FLOAT,GL_FALSE,6 * sizeof(float ) ,(void *)(2 * sizeof(float)) );//    // 启用色彩顶点数据//    glEnableVertexAttribArray(colorHandle);//    // 解除绑定//    glBindBuffer(GL_ARRAY_BUFFER,0);// ############################################# 分割线 #################################    // VBO与VAO配合绘制    // 应用vao    glBindVertexArray(vao);    // 4个顶点绘制两个三角形组成矩形    glDrawArrays(GL_TRIANGLE_STRIP,0,4);    glUseProgram(0);    // vao解除绑定    glBindVertexArray(vao);    // 禁用顶点    glDisableVertexAttribArray(positionHandle);    if(nullptr != eglHelper){        eglHelper->swapBuffers();    }}VBOVAOOpengl::~VBOVAOOpengl() noexcept {    glDeleteBuffers(NUM_VBO,vbo);    glDeleteVertexArrays(1,&vao);}

往期笔记

Opengl ES之EGL环境搭建
Opengl ES之着色器
Opengl ES之三角形绘制
Opengl ES之四边形绘制
Opengl ES之纹理贴图

关注我,一起提高,人生不止coding!!!