顶点缓存对象(VBO)与顶点数组对象(VAO)

时间:2024-09-29 19:34:17

         我们的顶点数组在CPU端的内存里是以数组的形式存在,想要GPU去绘制三角形,那么需要将这些数据传输给GPU。那这些数据在显存端是怎么存储的呢?VBO上场了,它代表GPU上的一段存储空间对象,表现为一个unsigned int类型的变量,GPU端内存对象的一个ID编号、地址、大小。一个VBO对象既代表了GPU端的一段区域。

 VBO的创建 

VBO的销毁

  // 1 创建一个vbo /*还没有真能正分配显存*/
  GLuint vbo = 0;
  GL_CALL(glGenBuffers(1, &vbo));
  // 2 销毁一个vbo
  GL_CALL(glDeleteBuffers(1, &vbo));
  // 3 创建n个vbo
  GLuint vboArr[] = { 0,0,0 };
  GL_CALL(glGenBuffers(3, vboArr));
  // 4 销毁n个vbo
  GL_CALL(glDeleteBuffers(sizeof(vboArr), vboArr));

VBO绑定+数据更新

 VBO绑定

VBO填入数据

        该函数会对现存进行销毁重建操作,需要谨慎调用。

多属性数据

        通常,一个三角形不仅仅有位置信息,也可能存在颜色、法向、纹理等数据。 这时候我们该怎么存储呢?

        针对以上问题,我们能想到创建两个vbo去存放。

        还有一种办法是将两种数据存储在一个buffer中,且数据是交叉的。 

        不管使用哪种方式,我们都应该对数据进行描述,以便GPU对数据进行合理的使用,否则在GPU端也不知道那几个数字代表一个顶点。所以我们需要告诉GPU每个顶点的布局。

什么是VAO?

VAO用来记录每种数据的描述信息的。每个描述信息都描述了某个信息,位置或者颜色。

对于位置颜色在同一个vbo时,我们应该如何去描述,让GPU能够理解这对数据?

对于一个vb中仅有位置数据时,只需要知道

  • 每个顶点有几个数据
  • 每个数字的类型
  • 每个顶点数据的步长
  • 该属性存储在几号vbo(1号)

只要知道这些数据即可准确的描述顶点数据。

对于一个vbo 中既有位置信息又有其他属性时

我们凭借以上信息不能准确地拿到某个属性数据,比如下图中的颜色信息。

位置数据处于每组数据的开头,颜色信息处于每组数据的其中,针对每组数据,位置信息存在一个固定的偏移量,这个偏移量就是每个顶点数据的大小。

综上,总结如下:

 

VAO的创建与删除

VAO加入VBO描述

DEMO1(顶点数据独立存放)

void prepareSingleBuffer()
{
  float positions[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
  };

  float colors[] = {
    1.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f,
    0.0f,  0.0f, 1.0f
  };
  // 1 生成一个vbo
  GLuint vboPos = 0, vboColor = 0;
  GL_CALL(glGenBuffers(1, &vboPos));
  GL_CALL(glGenBuffers(1, &vboColor));

  // 2 绑定当前vbo,到opengl状态机的当前vbo插槽
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboPos));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW));

  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboColor));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW));

  // 创建及绑定vao
  GLuint vaoPos = 0;
  GL_CALL(glGenVertexArrays(1, &vaoPos));
  GL_CALL(glBindVertexArray(vaoPos));
  // 描述为指数型
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboPos)); // 只有绑定了vbo,下面的属性描述才会有当前vbo有关系
  GL_CALL(glEnableVertexAttribArray(0));
  GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (GLvoid*)0));

  // 创建及绑定vao
  GLuint vaoColor = 0;
  GL_CALL(glGenVertexArrays(1, &vaoColor));
  GL_CALL(glBindVertexArray(vaoColor));
  // 描述为指数型
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboColor)); // 只有绑定了vbo,下面的属性描述才会有当前vbo有关系
  GL_CALL(glEnableVertexAttribArray(1));
  GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (GLvoid*)0));

  GL_CALL(glBindVertexArray(0));
}

DEMO(顶点数据混合存放) 

void prepareInterleavedBuffer()
{
  float vertices[] = {
    -0.5f, -0.5f, 0.0f,1.0f, 0.0f, 0.0f,
     0.5f, -0.5f, 0.0f,0.0f, 1.0f, 0.0f,
     0.0f,  0.5f, 0.0f,0.0f,  0.0f, 1.0f
  };

  GLuint vbo = 0;
  GL_CALL(glGenBuffers(1, &vbo));
  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
  GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));

  GLuint vao = 0;
  GL_CALL(glGenVertexArrays(1, &vao));
  GL_CALL(glBindVertexArray(vao));

  GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));

  GL_CALL(glEnableVertexAttribArray(0));
  GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)0));

  GL_CALL(glEnableVertexAttribArray(1));
  GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT))));

  GL_CALL(glBindVertexArray(0));
}