Render one VAO containing two VBOs
Asked Answered
G

1

6

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?

Gourmand answered 17/11, 2016 at 10:50 Comment(2)
The glVertexAttribPointer in the render loop look extremely suspicious. Part of the whole point of using VAOs is that you set up the inputs of the pipeline only once, then you do bind vao; draw; bind another vao; draw;. Why do you keep re-setting the attributes (to the same buffers/formats, nonetheless)?Selie
Second, how does your vertex shader look like? At which location(s) is it expecting inputs? And how could the shader know (if that's all of your code) that it needs to process attributes coming from one location instead of another, depending on which triangle you want to draw?Selie
D
11

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);
Damara answered 17/11, 2016 at 15:44 Comment(5)
Is there a way to bind the buffer with different primitive inside the VAO so I can avoid binding it before calling the glDrawArrays? It actually works if you have a single buffer: you can bind it in the VAO definition, and then in the loop binding the VAO and calling the draw it is just fine to have the job done. Now, I have 2 different VBOs (one for Lines primitive and one for LineLoop primitive, and I want to tell which VBO serves which primitive in the VAO definition... the binding the VAO in the loop and just call the 2 drawing functions. Is it possible?Ivonne
@MaxC: Neither VAOs nor buffer objects tell anyone anything about the "primitive mode"; that is a property of the drawing command. It's up to you how to sort that out.Damara
thanks a lot Nicol, however, this leads me to another point I can't figure out how to solve: instancing. I can't reconcile any old-style tutorial about instancing your guidelines about new APIs usage and the above fragment. I can't understand how to tell GL to use a different buffer to get model matrices and use both when I call DrawArraysInstanced.Ivonne
Maybe I figured it out, even though it is still not working, I think I'm on the way. In case I'll open a new clear question on the topic.Ivonne
The way was right but there is something missing. Here the question: #73176893Ivonne

© 2022 - 2024 — McMap. All rights reserved.