Search code examples
copenglglfwvbovao

Render one VAO containing two VBOs


I'm trying to draw two triangles in a window in OpenGL 3.3. I'm using the GLFW library for the windowing system.

From what I understand, I should have two VBOs (one for each triangle) and one VAO containing these two VBOs. That's what I did.

However, I can't figure out what calls I should make to render these two VBOs. Indeed, whatever I do, only the first VBO (first triangle) gets drawn. The second one never shows up.

int main()
{
    GLFWwindow *window = setupWindow();
    GLfloat triangle1Vertices[] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };
    GLfloat triangle2Vertices[] = {
        0.f, -0.5f, 0.0f,
        0.8f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };
    GLuint vao, vbo[2];

    glGenVertexArrays(1, &vao);
    glGenBuffers(2, vbo);

    glBindVertexArray(vao);

    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle1Vertices), triangle1Vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2Vertices), triangle2Vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();

        glClearColor(0.3f, 0.3f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);


        glBindVertexArray(vao);

        glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glBindVertexArray(0);


        glfwSwapBuffers(window);
    }

    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(2, vbo);

    glfwTerminate();

    return 0;
}

I'm clearly missing something, but what?


Solution

  • From what I understand, I should have two VBOs (one for each triangle) and one VAO containing these two VBOs.

    Then your understanding is wrong.

    If you want to have two different buffers with two different triangles, then you either need two different VAOs, or you need to swap which buffer the VAO uses between rendering each object.

    Your rendering code is close to correct, but not quite:

        glBindVertexArray(vao);
    
        glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
        glDrawArrays(GL_TRIANGLES, 0, 3);
    
        glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
        glDrawArrays(GL_TRIANGLES, 0, 3);
    

    Notice that the second glVertexAttribPointer call uses attribute 0 as well. The attribute indices you pass to glVertexAttribPointer specify which shader input variable gets fed from that array. What you're trying to do is render a second object using the same shader, changing only where the vertex arrays come from. And that requires using the same attribute index.

    However, if OpenGL 4.3/ARB_vertex_attrib_binding is available to you, I would highly encourage you to just use those APIs. It makes your code much more straightforward:

    //vertex setup
    glGenVertexArrays(1, &vao);
    
    glBindVertexArray(vao);
    //Sets up the format, *without* a buffer object.
    glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0);
    //Sets up where the buffer object comes from
    glVertexAttribBinding(0, 0);
    //Enable VAO
    glEnableVertexAttribArray(0);
    //Done with VAO
    glBindVertexArray(0);
    
    //Set up buffer object data storage.
    glGenBuffers(2, vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle1Vertices), triangle1Vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2Vertices), triangle2Vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    

    Then, in your loop:

    glBindVertexArray(vao);
    
    //Use buffer 0 to render.
    glBindVertexBuffer(0, vbo[0], 0, 3 * sizeof(float));
    glDrawArrays(GL_TRIANGLES, 0, 3);
    //Use buffer 1 to render.
    glBindVertexBuffer(0, vbo[1], 0, 3 * sizeof(float));
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    glBindVertexArray(0);