VBO、VAO和EBO
Vertex Buffer Object
对于经历过fixed pipeline的我来讲,VBO的出现对于渲染性能提升让人记忆深刻。完了,暴露年龄了~
//immediate mode
glBegin(GL_TRIANGLES);
glNormal3f(...);
glVertex3f(...);
glEnd();
//display list
list = glGenLists(1);
glNewList(list, GL_COMPILE);
glBegin(GL_TRIANGLES);
glNormal3f(...);
glVertex3f(...);
glEnd();
glEndList();
glCallList(list);
//vertex array
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(...);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(...);
glDrawArrays(...);
上面的代码是远古时期的OpenGL绘制图元的执行流程,不懂也不用追究了,因为实在太老了。
接下来我们进入正题。
VBO标识的是显卡中的一块存储区域,我们可以从内存中向它传送顶点数据(空间位置,纹理坐标,法线等等),然后在draw的时候作为vertex attribute进行使用。
void init()
{
GLfloat position[] = //空间位置
{
-0.8f, -0.8f, 0.0f,
0.8f, -0.8f, 0.0f,
0.0f, 0.8f, 0.0f
};
GLfloat color[] = //颜色
{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
GLuint vbo[2] = {0};
glCreateBuffers(2, vbo); //创建buffer对象
//把buffer object绑定到GL_ARRAY_BUFFER(binding target),表示这个buffer object用来存放vertex attributes,现在它就是一个VBO啦
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, 0);//为GL_ARRAY_BUFFER绑定一个无效的对象,防止后续的手贱操作,同时提醒你4.5版本引入DSA之后,可以在不bind的情况下直接操纵object了
glNamedBufferData(vbo[0], 9 * sizeof(GLfloat), position, GL_STATIC_DRAW); //向vertex buffer上传数据
glNamedBufferData(vbo[1], 9 * sizeof(GLfloat), color, GL_STATIC_DRAW);
}
至此,我们创建好了两个VBO并分别存放了空间位置数据和颜色数据。
我们的shader仍然是最简单的shader:
//vertex shader
#version 460 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 vs_out_color;
void main(void)
{
vs_out_color = color;
gl_Position = vec4(position, 1.0);
}
//fragment shader
#version 460 core
layout(location = 0) in vec3 fs_in_color;
layout(location = 0) out vec4 frag_color;
void main(void)
{
frag_color = vec4(fs_in_color, 1.0);
}
接下来的问题就是我们如何把VBO与vertex attribute (location) 关联起来,这时候VAO就闪亮登场了。
Vertex Array Object
GLuint vao = 0;
void init()
{
... //set up vbo
glCreateVertexArrays(1, &vao); //创建vao
//启用vertex attribute 0 和 1,其中0和1分别与vertex shader中的position(location = 0)和color(location = 1)对应
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glVertexArrayVertexBuffer(vao, 3, vbo[0], 0, sizeof(GLfloat) * 3); //设置vbo的binding point
glVertexArrayVertexBuffer(vao, 5, vbo[1], 0, sizeof(GLfloat) * 3);
glVertexArrayAttribBinding(vao, 0, 3); //设置vertex attribute的binding point,须与对应的vbo bind到同一个binding point上
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0); //指定vertex attribute的顶点规范,相当于告诉OpenGL如何解析对应的vbo数据,之后vertex shader就能够拿到正确的vertex attribute
glVertexArrayAttribBinding(vao, 1, 5);
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 0);
}
可以看到,自从4.5版本增加了DSA,API的执行顺序不是那么的重要了,因为调用OpenGL的命令需要显式的指定handle,而不是把这个handle绑定到当前的OpenGL Context(如上述代码,每次我们都传入了vao)。另外对于状态的从属关系,也更加明确了。
我们把vertex attribute和对应的vbo绑定到了同一个binding point上,相当于告诉OpenGL,vertex attribute的数据来自哪个vbo。这里我故意把binding point的值分别设置为3和5(其实可以设置为0和1),是担心有同学会把vertex attribute binding point和vertex attribute index弄混淆了。

另外需要注意的是glVertexArrayVertexBuffer最后一个参数指定了vbo中元素之间的stride,与glVertexAttribPointer不同的是,就算vbo中的元素是紧挨着的,也必须设置正确的stride值,而不能设置为0。因为在调用glVertexArrayVertexBuffer的时候,OpenGL对于vbo中的数据该如何解析丝毫不知情。而之所以你之前用到的glVertexAttribPointer的stride可以设置为0,是因为这个命令同时指定了每个元素的类型(比如GL_FLOAT)以及size(比如由3个GL_FLOAT组成),相当于OpenGL会自动帮我们去算正确的stride的值。有的同学可能会说,glVertexArrayAttribFormat不是指定了如何解析vbo中的数据吗,但是你有没有想过:glVertexArrayAttribFormat不一定在glVertexArrayAttribBinding之前调用,所以在调用glVertexArrayAttribBinding的时候OpenGL可能还不知道vbo的数据信息。
我们已经vao设置好了所有必要的信息了,现在用它进行render
void render()
{
...
glBindVertexArray(vao); //绑定vao
glDrawArrays(GL_TRIANGLES, 0, 3 ); //draw
glBindVertexArray(0); //draw完成,将当前context下的Vertex Array绑定到一个无效的handle上
}
VAO先讲到这里,下面我们看一下EBO。
Element Buffer Object
所谓EBO,就是把顶点索引数据保存到buffer中,然后用这些索引去vertex buffer中查找对应的顶点来绘制图元,以避免在vertex buffer中存放冗余的顶点信息。
//绘制一个矩形
void init()
{
GLfloat position[] =
{
-0.5f, 0.5f,
-0.5f, -0.5f,
0.5f, 0.5f,
0.5f, -0.5f,
};
GLfloat color[] =
{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f
};
GLubyte index[] =
{
0, 1, 3,
2, 0, 3
};
glCreateBuffers(2, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glNamedBufferData(vbo[0], 8 * sizeof(GLfloat), position, GL_STATIC_DRAW);
glNamedBufferData(vbo[1], 12 * sizeof(GLfloat), color, GL_STATIC_DRAW);
glCreateBuffers(1, &ebo); //创建buffer对象
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); //将buffer对象当作GL_ELEMENT_ARRAY_BUFFER
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //为当前的OpenGL Context的EBO置为无效值
glNamedBufferData(ebo, 6 * sizeof(GLubyte), index, GL_STATIC_DRAW); //向element array buffer传输索引数据
glCreateVertexArrays( 1, &vao );
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glVertexArrayVertexBuffer(vao, 3, vbo[0], 0, sizeof(GLfloat) * 2);
glVertexArrayVertexBuffer(vao, 5, vbo[1], 0, sizeof(GLfloat) * 3);
glVertexArrayAttribBinding(vao, 0, 3);
glVertexArrayAttribFormat(vao, 0, 2, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(vao, 1, 5);
}
同学们只需要关注我加注释的那一段代码。可以发现,其实EBO的建立过程与VBO极为类似。
不过很遗憾,本来我以为可以同vbo一样,通过binding point或者其它手段建立EBO和VAO的联系,可惜没找到。所以我们在render的时候,除了要绑定VAO外,还要把用到的EBO绑定至当前的OpenGL Context。
void render()
{
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); //绑定ebo
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)(nullptr));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
draw命令也不能用glDrawArrays,而是用glDrawElements。现在我确信不存在可以建立EBO和VAO之间联系的API了,因为glDrawElements的最后的两个参数分别表示EBO存放的数据类型和起始位置的字节偏移。如果存在这样的API,那么这两个参数的信息肯定是保存到了VAO中了(参照VBO和VAO)。
小结:
- VBO表示显卡中用于存放vertex attribute数据的一块缓存。
- VAO通过vertex attribute binding point建立vertex attribute index与VBO之间的联系,并且在render的时候,只需要绑定一个VAO即可进行draw,减少了状态切换,提升渲染性能。
- EBO以索引的形式对VBO中的顶点进行多次利用,但是无法建立EBO与VAO之间的联系,所以每次draw之前,需要显式的绑定EBO。
VBO、VAO和EBO的更多相关文章
- [转]OpenGL图形渲染管线、VBO、VAO、EBO概念及用例
直接给出原文链接吧 1.OpenGL图形渲染管线.VBO.VAO.EBO概念及用例 2.OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及 ...
- LearnOpenGL学习笔记(三)——VBO,VAO,EBO理解
在opengl中所有的数据都要放在显存中,我们通过一定的手段去管理它,既要提供地方存放它,还要提供方法去正确地提取它们,去使用它们,opengl通过VAO,VBO,EBO这些手段来解决这些问题. (一 ...
- OpenGL图形渲染管线、VBO、VAO、EBO概念及用例
图形渲染管线(Pipeline) 图形渲染管线指的是对一些原始数据经过一系列的处理变换并最终把这些数据输出到屏幕上的整个过程. 图形渲染管线的整个处理流程可以被划分为几个阶段,上一个阶段的输出数据作为 ...
- VBO, VAO, Generic Vertex Attribute
VBO - 用于存储顶点数据的Buffer Object. VAO - 用于组织VBO的对象. Generic Vertex Attribute - 通用顶点属性. For example, the ...
- Opengl ES之FBO
FBO介绍 FBO帧缓冲对象,它的主要作用一般就是用作离屏渲染,例如做Camera相机图像采集进行后期处理时就可能会用到FBO.假如相机出图的是OES纹理,为了方便后期处理, 一般先将OES纹理通过F ...
- OpenGL渲染管道,Shader,VAO&VBO&EBO
OpenGL渲染管线 (也就是)OpenGL渲染一帧图形的流程 以下列举最简单的,渲染一个三角形的流程,你可以将它视为 精简版OpenGL渲染管线 更复杂的流程也仅仅就是:在此基础上的各个流程中 添加 ...
- VAO VBO IBO大乱炖
最近对程序中绘制卡顿的问题忍无可忍,终于决定下手处理了.程序涉及的绘制比较多,除了点.线.三角形.多边形.圆柱体之外,还有自组格式模型.开始想全部采用显示列表优化,毕竟效率最高,虽然显示列表存在编译之 ...
- GLES2学习VBO和VAO的使用
在GLES2中使用VBO和VAO对象,已经简单vs,ps绘制一个三角形. 1. 初始化操作代码,创建VBO.VAO,编译和链接shader program. void DebugApplication ...
- Opengl ES之VBO和VAO
前言 本文主要介绍了什么是VBO/VAO,为什么需要使用VBO/VAO以及如何使用VBO和VAO. VBO 什么是VBO VBO(vertex Buffer Object):顶点缓冲对象.是在显卡存储 ...
随机推荐
- SPOJ QTree【树链剖分】
一 题目 QTREE 二 分析 第一道树链剖分的题,写的好艰难啊. 题意还是比较好理解的,就是在树上操作. 对于修改,题中要求的是单点修改,就算是直接树上操作也是非常简单的. 对于查询,查询的时候,是 ...
- incubator-dolphinscheduler 如何在不写任何新代码的情况下,能快速接入到prometheus和grafana中进行监控
一.prometheus和grafana 简介 prometheus是由谷歌研发的一款开源的监控软件,目前已经贡献给了apache 基金会托管. 监控通常分为白盒监控和黑盒监控之分. 白盒监控:通过监 ...
- 想了很久,一道Microsoft的笔试题目 —— Reversing Linked List
Reversing Linked List Given a constant K and a singly linked list L, you are supposed to reverse the ...
- 攻防世界 reverse 进阶5-7
5.re-for-50-plz-50 tu-ctf-2016 流程很简单,异或比较 1 x=list('cbtcqLUBChERV[[Nh@_X^D]X_YPV[CJ') 2 y=0x37 3 z= ...
- 力扣 - 560. 和为K的子数组
目录 题目 思路1(前缀和) 代码 复杂度分析 思路2(前缀和+哈希表优化) 代码 复杂度分析 题目 560. 和为K的子数组 思路1(前缀和) 构建前缀和数组,可以快速计算任意区间的和 注意:计算区 ...
- markdown快捷输入
标题: 输入方式:#+空格+标题名,几级标题就敲几个# 注:最大支持六级标题 字体 加粗:在要加粗的字体前后加** 斜体:在要倾斜的字体前后加* 斜体加粗:在要倾斜并加粗的字体前后加*** 删除线:在 ...
- python工业互联网应用实战11—客户端UI
这个章节我们将演示用户端界面的开发,当前演示界面还是采用先实现基本功能再逐步完善的"敏捷"模式.首先聚焦在功能逻辑方面实现普通用户与系统的交互,普通用户通过url能查看到当前任务的 ...
- C#修改AD账号及密码
在使用AD域环境搭建的账号系统修改密码的时候比较麻烦一般需要管理员在域环境去进行对用户的密码进行修改. 以下就是用来查询和修改AD域密码的方法. 1 /// <summary> 2 /// ...
- 字节、位、bit、byte、KB、B、字符之间的关系
一.bit 位bit就是位,也叫比特位,是计算机表示数据最小的单位,例如 1b ,2b,3b..... 说白了就是0或者1:计算机内存中的存储都是01这两个东西. 二.byte 字节byte就是字节 ...
- es的分布式架构原理能说一下么(es是如何实现分布式的啊)?
在搜索这块,lucene是最流行的搜索库.几年前业内一般都问,你了解lucene吗?你知道倒排索引的原理吗?现在早已经out了,因为现在很多项目都是直接用基于lucene的分布式搜索引擎--elast ...