VAO 与 VBO 和 IBO 的手动绑定

Posted

技术标签:

【中文标题】VAO 与 VBO 和 IBO 的手动绑定【英文标题】:VAO vs Manual Binding of VBO's and IBO 【发布时间】:2014-05-05 20:15:38 【问题描述】:

我一直在玩 OpenGL 一段时间,想熟悉顶点数组对象(VAO)。我正在使用立方体示例here 进行测试。首先我验证了我可以通过手动绑定两个顶点缓冲区和元素数组缓冲区以及顶点属性来绘制一个立方体这是我的代码

    void Project::initTest(void)
    
        GLfloat cube_vertices[] = 
        // front
        -1.0, -1.0,  1.0,
        1.0, -1.0,  1.0,
        1.0,  1.0,  1.0,
        -1.0,  1.0,  1.0,
         // back
         -1.0, -1.0, -1.0,
         1.0, -1.0, -1.0,
         1.0,  1.0, -1.0,
         -1.0,  1.0, -1.0,
        ;
        glGenBuffers(1, &vbo[0]);
        glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW);

        GLfloat cube_colors[] = 
        // front colors
        1.0, 0.0, 0.0,
        0.0, 1.0, 0.0,
        0.0, 0.0, 1.0,
        1.0, 1.0, 1.0,
        // back colors
        1.0, 0.0, 0.0,
        0.0, 1.0, 0.0,
        0.0, 0.0, 1.0,
        1.0, 1.0, 1.0,
        ;

        glGenBuffers(1, &vbo[1]);
        glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(cube_colors), cube_colors, GL_STATIC_DRAW);

         GLushort cube_elements[] = 
             // front
             0, 1, 2,
             2, 3, 0,
             // top
             3, 2, 6,
             6, 7, 3,
             // back
             7, 6, 5,
             5, 4, 7,
             // bottom
             4, 5, 1,
             1, 0, 4,
             // left
             4, 0, 3,
             3, 7, 4,
             // right
             1, 5, 6,
             6, 2, 1,
         ;

     glGenBuffers(1, &ibo);
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_elements), cube_elements,      GL_STATIC_DRAW);
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
         glBindBuffer(GL_ARRAY_BUFFER,0);

       

 

这里是绘制函数


void Project::drawTest()

  glEnableVertexAttribArray(0);
  glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
  glVertexAttribPointer(0, 3,GL_FLOAT,GL_FALSE,0,0);

  glEnableVertexAttribArray(1);
  glBindBuffer(GL_ARRAY_BUFFER, 1);
  glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,0);

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
  glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);


结果是一个立方体。

使用顶点数组对象 这是我将缓冲区打包到 VAO 中的实现。 initTest() 代码如下:


void Project::initTest()

  //Upload Vertex Data into VBO's
  //Upload Index Data into Element Array Buffer 
  //...
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);//Position Buffer
    glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);//Colour Buffer
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(1); 
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ibo);
    glBindVertexArray(0);

        //Unbind ARRAY and ELEMENT_ARRAY Buffers like before

然后在我的绘图函数中,我只是像这样调用 VAO


void Project::drawDebug()

    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES,36, GL_UNSIGNED_SHORT, 0);
    glBindVertexArray(0);

但是结果是这样的:

最终,我想知道我哪里出了问题。我还想知道您是否在当前绑定的 VAO 中绑定了 ELEMENT_ARRAY_BUFFER,是否需要在调用 glDraw* 之前显式地重新绑定该 ELEMENT_ARRAY_BUFFER,或者 VAO 是否检测到它具有索引缓冲区并使用索引进行渲染。

【问题讨论】:

【参考方案1】:

这里出错了:

glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);//Position Buffer
glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);//Colour Buffer
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1); 
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

这与 VAO 完全无关,如果没有 VAO,也会以同样的方式失败。

在切换到 VAO 时,您似乎也不再使用两个单独的 VBO 作为属性。你当然可以这样做,而且你可以在没有 VAO 的情况下这样做。但是你必须正确地做到这一点:glVertexAttribPointer() 将使 当前绑定 GL_ARRAY_BUFFER 成为属性指针的一部分,在 两种情况下 都是 vbo[1]。所以你使用颜色数据作为颜色和位置。

但是,您的原始代码也是错误的:它仅使用位置数据作为颜色和位置(与将颜色值限制在 [0,1] 范围内一起导致您获得的不对称颜色)。

正如我的旁注:每当您看到两个 GL 绑定操作对同一个绑定目标紧接着进行时,第一个总是无用的。

所以正确的代码应该是(如果你现在正在使用两个不同的 VBO,这还不清楚):

glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);//Position Buffer
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER,vbo[1]);//Colour Buffer
glEnableVertexAttribArray(1); 
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

【讨论】:

就是这样!现在对我来说很有意义。我想由于在这种情况下我的数据都是静态的,我可能应该将顶点数据打包到一个 VBO 中。谢谢。 使用完全静态的数据,使用单个 VBO 和interleaved attributes 时可能会获得最佳性能。

以上是关于VAO 与 VBO 和 IBO 的手动绑定的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 上使用具有多个 VBO 和 IBO(多个对象)OpenGLES 2 的 VAO 进行绘制

两个不同的对象 OpenGL。 VAO VBO IBO 网格变形问题

VAO 中的 C++ GLSL 多个 IBO

通过 IBO 了解 VAO

一个 VBO 可以绑定多个 VAO 吗?

使用 IBO/EBO 时,程序仅在我调用 glBindBuffer 以在创建 VAO 后绑定 IBO/EBO 时才有效