夏天/isummer

Sun of my life !Talk is cheap, Show me the code! 追风赶月莫停留,平芜尽处是春山~

博客园 首页 新随笔 联系 管理

比较常用的时:VBO配合IBO使用。

 

OpenGL理解VBO,IBO,VAO:https://blog.csdn.net/Mhypnos/article/details/123516615

 

 

VBO,VAO和EBO详解:https://www.jianshu.com/p/c7833cd9553a

Opengl:EBO和IBO的使用:https://blog.csdn.net/weixin_44629261/article/details/145257838

 

VBO:VBO(Vertex Buffer Objects)顶点缓冲区对象,需要绑定缓存类型:GL_ARRAY_BUFFER 指的是在 GPU 显存里面存储的顶点数据(位置、颜色,纹理等)。仅仅时GPU内存中存放的时顶点各种属性

IBO:(Index Buffer Objects) 顶点索引缓冲区对象,需要绑定缓存类型:GL_ELEMENT_ARRAY_BUFFER。 可以利用点的索引,来构建三角面面片,减少上传GPU中点的数量。使用基于索引的绘图机制。

EBO:Element Buffer Object  元素缓存对象,需要绑定缓存类型:GL_ELEMENT_ARRAY_BUFFER。 于存储顶点索引,避免重复存储相同顶点,减少内存消耗。

VAO:Vertex Array Object 顶点数组对象,就是保存VBO(以及IBO,如果用的话)中的属性配置。

 

二、void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices);
1、从索引缓冲区渲染模型,即从当前绑定到GL_ELEMENT_ARRAY_BUFFER目标的EBO中获取其索引。
2、mode指定要绘制的图元类型。
3、count指定要绘制的顶点个数。
4、type指定索引数据类型。
5、indices即可以指定EBO的偏移量(使用EBO),也可以传递一个索引数组(不使用EBO,不建议)。
 

 

总结:VBO,VAO,EBO之间的联系与区别

 

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

  所以可以理解为 VBO 就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个 VBO ,每个 VBO 在 OpenGL 中有它的唯一标识 ID ,这个 ID 对应着具体的 VBO 的显存地址,通过这个 ID 可以对特定的 VBO 内的数据进行存取操作。

  • VBO就像一个仓库,专门存放3D模型所需要的原材料(顶点数据)。所有材料都是提前存放到仓库中的,使用时可以直接取用,无需临时搬运。
  • 支持批量数据传输,例如一次性存储万个顶点数据
  • 避免每帧将顶点数据从CPU传输到GPU,提升渲染性能

  ② VAO 是一个保存了所有顶点数据属性的状态结合,它存储了顶点数据的格式以及顶点数据所需的 VBO 对象的引用,记录GPU如何从与之配对的VBO中读取数据
   因为 VBO 保存了一个模型的顶点属性信息,每次绘制模型之前需要绑定顶点的所有信息。当数据量很大时,重复这样的动作变得非常麻烦。VAO 可以把这些所有的配置都存储在一个对象中,每次绘制模型时,只需要绑定这个 VAO 对象就可以了。

  另外,VAO 本身并没有存储顶点的相关属性数据,这些信息是存储在 VBO 中的,VAO 相当于是对很多个 VBO 的引用,把一些 VBO 组合在一起作为一个对象统一管理。

  • 简化顶点属性配置流程,避免每次绘制重复设置
  • 绑定VAO后自动恢复关联的VBO和属性配置
  • VAO就像一本说明书,告诉工人(GPU)如何从仓库(VBO)中取出材料,并按什么规格(数据类型、偏移量)组装零件(顶点)

 ③ 索引缓冲对象 EBO(Element Buffer Object 索引缓冲对象),  相当于 OpenGL 中的顶点数组的概念,是为了解决同一个顶点多次重复调用的问题,可以减少内存空间浪费,提高执行效率。当需要使用重复的顶点时,通过顶点的位置索引来调用顶点,而不是对重复的顶点信息重复记录,重复调用。
   EBO 中存储的内容就是顶点位置的索引 indices,EBO 跟 VBO 类似,也是在显存中的一块内存缓冲器,只不过 EBO 保存的是顶点的索引。

 

为什么使用EBO

使用场景:

  绘制复杂网格模型,同一顶点会被多个图元共享,用索引来标记每个三角形用到哪个顶点,而不用相同顶点重复创建

优点

  1. 节省内存。
  2. 减少顶点处理次数,提升缓存效效率
没有EBO代码,需要重复保存多个顶点数据:

float vertices[] = {
    // 第一个三角形
    0.0f,  0.5f, 0.0f,
   -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    // 第二个三角形
    0.5f, -0.5f, 0.0f,
    1.0f,  0.0f, 0.0f,
    0.0f,  0.5f, 0.0f
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

上面用到的顶点都直接上传GPU了(尽管优点顶点重复使用)。可以很清楚的看到,上面的例子中,两个三角形有一些共用的顶点,但是还是重新写了一遍,就很浪费,所以这时候就要EBO登场了。
-------------------------------------------------------
使用 EBO 的代码

float vertices[] = {
    0.0f,  0.5f, 0.0f,
   -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    1.0f,  0.0f, 0.0f
};
// 用索引来标记每个三角形用到的哪个顶点
unsigned int indices[] = {
    0, 1, 2,
    2, 3, 0
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

unsigned int ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);//绑定ebo,激活ebo
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//向CPU分配并初始化该ebo的内存

用一个indices的数组指定了每个图元使用的顶点的顺序就好了,这样当在有大量重复顶点的时候,就很省了。

 

为什么使用VAO()?

 VAO ( Vertex Array Object )是OpenGL用来处理顶点数据的一个缓冲区对象,它不能单独使用,都是结合VBO来一起使用的。

当我们使用VBO传入顶点数据时,一般的处理如下:

// 绑定VBO,设置VBO中的数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 1. 设置顶点属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);  
// 2. 使用Shader程序
glUseProgram(shaderProgram);
// 3. 绘制
someOpenGLFunctionThatDrawsOurTriangle();  

  注意,以上不适用VAO也可以,但是:每次都要如上配置:顶点属性以及激活顶点属性。麻烦:

     

      每当我们绘制一个几何体时,我们需要重复同样的工作(首先绑定缓冲区、然后设置顶点属性)。因为,没有顶点属性的配置,GPU不知道VBO如何使用。

  当需要绘制的物体很多时,这个过程就显得有些耗时。那么我们有没有一种方式来简化这一过程呢?这就是VAO做的事情,它将所有顶点绘制过程中的这些设置和绑定过程集中存储在一起,当我们需要时,只需要使用相应的VAO即可。

  VAO 本质上是状态记录器,是一种管理 OpenGL 顶点属性配置的对象,用于记录 VBO 和 EBO 的绑定状态及顶点属性设置,避免重复配置。(通俗点的讲,就是VAO只是个指针而已,实际干活的人还是VBO和EBO)

 

使用场景

  1. 场景复杂,有多个对象需要渲染时,使用 VAO 可以减少顶点属性配置的复杂度。
  2. 多次绘制同一个对象,减少状态切换。

优点

1. 减少状态切换。
将所有顶点属性配置打包到一个对象中,后续渲染时只需绑定 VAO 而不是重新配置,避免重复调用 glVertexAttribPointer。
2.简化代码。
管理多个 VBO、EBO,逻辑清晰。
3.提升性能。
减少 OpenGL 状态变更调用的次数。

 

 

VAO包含的内容

1. VAO开启或者关闭的状态(glEnableVertexAttribArray和glDisableVertexAttribArray)
2. 使用glVertexAttribPointer对顶点属性进行的设置
3. 存储顶点数据的VBO对象

 使用如下:

//创建VAO
GLuint VAO;
glGenVertexArrays(1, &VAO);
//设置当前VAO,之后所有操作(注意:这些操作必须是上文VAO中包含的内容所注明的调用,其他非VAO中存储的内容即使调用了也不会影响VAO)存储在该VAO中
glBindVertexArray(VAO);
   glBindBuffer(GL_ARRAY_BUFFER, VBO); //设置了VBO
   glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//设置VBO中的数据
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); //设置顶点属性(索引为0的属性,与shader中的内容有交互)
    glEnableVertexAttribArray(0); //设置开启顶点属性(索引为0的属性,与shader中的内容有交互)
glBindVertexArray(0); //解绑VAO(解绑主要是为了不影响后续VAO的设置,有点类似于C++中指针delete后置空,是个好习惯)

通过上面的代码就完成了对VAO的设置,当我们需要绘制的时候,使用的代码类似于:

场景绘制:
glUseProgram(shaderProgram); glBindVertexArray(VAO); //(再次使用)绑定我们需要的VAO,会导致上面所有VAO保存的设置自动设置完成 someOpenGLFunctionThatDrawsOurTriangle(); glBindVertexArray(0); //解绑VAO

 

 

 

 

 

此外,注意不同Opengl版本的API的变化

 

posted on 2025-07-10 16:46  夏天/isummer  阅读(162)  评论(0)    收藏  举报