前言
本文次要介绍了什么是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" "}";
- 顶点坐标与色彩值坐标拆散的形式(数组构造)
先看顶点数据与色彩数据:
// 应用绘制两个三角形组成一个矩形的模式(三角形带)// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形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);
- 顶点坐标与色彩值坐标联合的形式(构造数组)
顶点数据与色彩数据混合打乱:
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!!!