Qt-OpenGL VAO, VBO, IBO简析
本文最后更新于:2023年3月6日 晚上
前情提要
众所周知,初入OpenGL
炼狱,便是一坨让人不明所以、一脸懵逼的名词
比如什么:VAO
、VBO
、IBO
啊,对,就你们仨,枪打出头鸟啊,就你们仨打头阵啊,还都以O结尾啊
那就干你们了
VBO(Vertex Buffer Object :顶点缓冲对象)
这一听就和顶点有关,实际上就是用来存储顶点相关信息的(坐标、颜色、贴图坐标、法向量等)
这个对象可以把顶点相关数据一次性发送到GPU
,减少与CPU
通信开销
毕竟三维世界也需要二维的线和一维的点进行构建嘛
所以顶点信息是最基本的
比如这样:
1 |
|
每一行是一个顶点的信息,前两个数代表二维坐标,后两个数代表贴图坐标(贴图与顶点的对应关系)
但是你会发现一个奇怪的点,为什么我要用一维数组呢,酱紫不是没有任何结构(struct or class)吗?
一点也不面向对象啊,这是因为OpenGL
提供的API
就只支持一维数组,并且需要手动解释其中的数据(区分哪些是坐标、哪些是颜色、贴图之类的)
类似于:
1 |
|
顶点数据是怎么解释的呢,就是通过glEnableVertexAttribArray
这个函数
1 |
|
比如这一句:
1 |
|
ta的意思就是:
对于0号属性(也就是坐标,数字不重要 不重复就行),由相邻的两个元素组成(二维坐标),类型是GL_FLOAT
(浮点型),不开启归一化(GL_FALSE),两个数据(坐标)之间的步长是4个float
字节的长度,并且距离数组起点的偏移量是0
对于贴图坐标,也可以酱紫解释(命名为1号属性),这里属性的顺序啥的都不重要,只要在shader
(着色器)中能自己对应上就可以
Why
为什么要如此麻烦呢,为什么不给一些类和结构,而是从一维数组,原始数据开始解释呢
推测:OpenGL
是底层图形库,酱紫可以提供最高的性能以及自由度,供程序员自由封装;同时也对GPU
来讲更好读取
IBO (Index Buffer Object:索引数组对象) or EBO(Element Buffer Object)
其实有了VBO
就可以开始绘制图形了,为啥还要IBO
呢
IBO
也叫EBO
,两种叫法,但是第一种更见名知意,一看就知道是保存索引的
IBO
的存在是为了优化内存
比如:我们要画一个正方形,众所周知,一个正方形可以由两个三角形构成(对角分)
两个三角形有6个顶点,但是正方形只需要4个顶点
如此一来,有两个顶点的数据就是重复的,在VBO
里包含6个顶点的信息显然是十分滴stupid
那为何不直接用索引来代表顶点呢,比如给四个顶点编号(1,2,3,4)
然后告诉OpenGL
,我要绘制两个三角形,一个是(1,2,4),一个是(2,3,4)
酱紫可不方便多了
保存索引的对象就叫IBO
,索引缓冲对象
1 |
|
VAO(Vertex Array Object:顶点数组对象)
诶诶,有数据有索引了,还优化内存了,你还想咋样a,aaaaaa
是酱紫,兄弟,我知道你很急,但是你先别急
你看a,一组数据和一组索引可以绘制一个图形,但是假如有很多图形,每次切换着绘制,绘制前都要绑定这绑定那的有点麻烦
不如来一个东东来记录一下我们的操作(比如对顶点信息的解释,比如顶点数据)
这个东东就是VAO
,可以保存我们的操作,酱紫就可以只绑定VAO
实现绘制(通过VAO
访问各种数据)
具体保存了啥呢?
vertex attribute 对应的 VBO 的id,glBindBuffer 设置。
vertex attribute 的格式,由 glVertexAttribPointer 设置
vertex attribute 的开启或关闭,glEnableVertexAttribArray和glDisableVertexAttribArray
**#当前#**绑定的 GL_ELEMENT_ARRAY_BUFFER(索引缓冲数组,IBO) 的名字,由 glBindBuffer 设置
也就是保存了VBO
和IBO
,但是他们之间有一个很大的区别
- VBO是通过
glVertexAttribPointer
这个函数进行绑定的 - 而IBO是通过
glBindBuffer
函数进行绑定的
这是因为一个VAO
可以绑定多个VBO
(可以用不同VBO保存不同数据(坐标、颜色可以分开))
但是只能绑定一个IBO
(索引)
// 绑定0号缓冲区就是解绑:VBO: glBindBuffer(GL_ARRAY_BUFFER, 0) or IBO: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
所以VBO的解绑并不会影响VAO对其数据的访问(已经通过glVertexAttribPointer
保存了指针),而IBO的解绑就会导致VAO无法访问索引(变成0号IBO了)
深入VAO
实际上,在现代OpenGL
中,在VAO
出现后(v3.0),必须使用VAO才能绘制
如果你不显示地定义一个VAO
,那么默认(兼容模式)会提供的一个默认VAO
以供绑定;//核心模式不提供
所以,我们可以简单地这么想:
OpenGL
的draw
绘制函数就是从当前的VAO中读取VBO和IBO信息,并进行绘制的
那么,只要在绘制前,VAO中绑定了IBO,就可以正常运行
而在定义IBO时,并不需要绑定VAO
所以我们可以酱紫:
初始化部分:伪代码
1 |
|
绘制部分:伪代码
1 |
|
哦对,还想起个问题,为啥VAO
这名嫩奇怪啊,顶点数组对象?
其实是酱紫,我们可以吧ta看成一个数组,每一个元素都是一组顶点数据,通过glVertexAttribPointer
设置
比如VAO[0]
是坐标,VAO[1]
是颜色,VAO[2]
是法向量酱紫(当然通过[]访问是为了好理解)
Peace
Ref
OpenGL理解VBO,IBO,VAO_vbo和ibo_Mhypnos的博客-CSDN博客
[Modern OpenGL]谈谈VAO、VBO、IBO_SixDayCoder的博客-CSDN博客
OpenGL VAO VBO EBO(IBO)的绑定、解绑问题_解绑vbo,veo_csu_xiji的博客-CSDN博客
特别鸣谢:New Bing
& ChatGPT