OpenGL基础概念理解(1) VAO/VB0/EBO

2,712 阅读5分钟

简介

对于OpenGL而言,一个模型所需要的所有顶点信息(例如:位置,法线,贴图...)都需要放在GPU的显存里,如何高效的管理且有序的管理这些数据便是OpenGL所考虑的问题,了解了OpenGL管理这些数据的方式,我们也就明白了VAO/VBO/EBO三者之间的区别与联系。 image.png 如图所示,这是在blender中建模的一个模型,由四个顶点构成了两个三角形,将其导出成obj格式后,并且用txt格式打开便可以看到下面的数据。 image.png

首先我们来解读下图中的信息:

  • O : 文件名称
  • V : 顶点位置信息
  • Vt: 顶点uv信息
  • Vn:平面法线信息
  • f:平面的顶点构成 对于一些稍微有美术经验的程序员来说,当他打开obj格式文件,模型的大致信息他就一目了然,哪些数据属于顶点位置信息,哪些数据属于顶点的uv信息...可是对于GPU来说,它得到的数据是由一串连续数字组成的长字符,这串字符包含了一个模型的所有数据,它们的杂糅的组织在一起,并且晦涩难懂。
-1.000000 0.000000 1.000000 0.000000 0.065193 1.000000 0.751341 1.000000  1.000000 0.000000.......

理解了上述概念后,便可以知道OpenGL一系列方法和一组对象具体的功能和作用了,这里先给出一张本人的概念理解图便于大家总体上的理解 image.png

  • VBO相当于上述杂乱无章的字符串
  • VAO就相当于GPU能看懂的obj格式,
  • EBO就相当于obj格式中的f规定哪些点规定一个三角形,可以避免顶点的重复设置(比如一个正方形若不引用EBO,则需要六个顶点构成两个三角形,若引入EBO则只需要四个顶点就可以构成两三角形。)

顶点缓冲对象(Vertex Buffer Objects,VBO)

顶点缓冲对象VBO是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标,顶点法向量,顶点颜色数据等。在渲染时,可以直接从VBO中取出顶点的各类属性数据,由于VBO在显存而不是在内存中,不需要从CPU传输数据,处理效率更高。

image.png

VBO的创建与配置

  1. 创建VBO的第一步需要开辟(声明/获得)显存空间并分配VBO的ID:
  2. 把创建的VBO缓冲对象绑定到对应的缓冲类型上。
  3. 将Cpu中的顶点数据传送到该缓冲类型上。 代码如下所示:
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

顶点数组对象(Vertex Arrary Object,VAO)

VBO保存了一个模型的顶点属性信息,每次绘制模型之前需要绑定顶点的所有信息,当数据量很大时,重复这样的动作变得非常麻烦。VAO可以把这些所有的配置都存储在一个对象中,每次绘制模型时,只需要绑定这个VAO对象就可以了。 VAO是一个保存了所有顶点数据属性的状态结合,它对应上一个VBO和一个EBO(如果存在)。VAO中的数据才是顶点着色器中获取数据输入的地方 image.png

VAO的创建与配置

  1. 创建VAO的第一步需要开辟(声明/获得)显存空间并分配VAO的ID:
  2. 绑定VAO
  3. 从VBO中获取对应数据到VAO对应的attribute point上面
  4. 启用对应的attribute point 代码如下所示:
unsigned int VAO; 
glGenVertexArrays(1, &VAO);
// ..:: 初始化代码(只运行一次 (除非你的物体频繁改变)) :: .. 
// 1. 绑定VAO
glBindVertexArray(VAO); 
// 2. 把顶点数组复制到缓冲中供OpenGL使用 
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); 
[...]

索引缓冲对象(Element Buffer Object,EBO)

索引缓冲对象EBO相当于OpenGL中的顶点数组的概念,是为了解决同一个顶点多洗重复调用的问题,可以减少内存空间浪费,提高执行效率。当需要使用重复的顶点时,通过顶点的位置索引来调用顶点,而不是对重复的顶点信息重复记录,重复调用。 image.png

EBO的创建与配置

EBO的创建类似于VBO

  1. 创建EBO的第一步需要开辟(声明/获得)显存空间并分配EBO的ID:
  2. 把创建的EBO缓冲对象绑定到对应的缓冲类型上。
  3. 将Cpu中的顶点索引数据传送到该缓冲类型上。 代码如下所示:
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

当用EBO绑定顶点索引的方式绘制模型时,需要使用glDrawElements而不是glDrawArrays: glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

重点函数详解

1. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

  • 参数1:填充的缓冲对象类型,里面已经绑定了一个对应的缓冲对象
  • 参数2:填充的数据大小
  • 参数3:填充的数据来源
  • 参数4;显卡如何管理我们的数据
    • GL_STATIC_DRAW :数据不会或几乎不会改变。
    • GL_DYNAMIC_DRAW:数据会被改变很多。
    • GL_STREAM_DRAW :数据每次绘制时都会改变。

2. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void)0);*

  • 参数1:指定我们要配置的顶点属性,与顶点着色器中layout(location=0)对应。
  • 参数2:指定顶点属性的大小。顶点属性是一个vec3,它由3个值组成,所以大小是3。
  • 参数3:指定数据的类型,这里是GL_FLOAT(GLSL中vec*都是由浮点数值组成的)。
  • 参数4:是否希望数据被标准化(Normalize)。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。
  • 参数5:指定连续的顶点属性组之间的间隔。它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在3个float之后,我们把步长设置为3 * sizeof(float)
  • 参数6:指定我们的位置数据在缓冲区起始位置的偏移量。

参考