渲染一个包含两个 VBO 的 VAO

Posted

技术标签:

【中文标题】渲染一个包含两个 VBO 的 VAO【英文标题】:Render one VAO containing two VBOs 【发布时间】:2016-11-17 10:50:00 【问题描述】:

我正在尝试在 OpenGL 3.3 的窗口中绘制两个三角形。我正在为窗口系统使用 GLFW 库。

据我了解,我应该有 两个 VBO(每个三角形一个)和包含这两个 VBO 的 一个 VAO。我就是这么做的。

但是,我不知道应该调用什么来渲染这两个 VBO。事实上,无论我做什么,只有 第一个 VBO(第一个三角形)被绘制。第二个永远不会出现。

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;

我显然错过了什么,但是什么?

【问题讨论】:

渲染循环中的glVertexAttribPointer 看起来非常可疑。使用 VAO 的部分原因在于,您只需设置管道的输入一次,然后您就执行 bind vao; draw; bind another vao; draw;。你为什么要不断地重新设置属性(仍然是相同的缓冲区/格式)? 第二,你的顶点着色器是什么样子的?它期望在哪个位置输入?着色器如何知道(如果这是您的全部代码)它需要处理来自一个位置而不是另一个位置的属性,具体取决于您要绘制哪个三角形? 【参考方案1】:

据我了解,我应该有两个 VBO(每个三角形一个)和一个包含这两个 VBO 的 VAO。

那你的理解是错误的。

如果您想要两个不同的缓冲区和两个不同的三角形,那么您需要两个不同的 VAO,或者您需要在渲染每个对象之间交换 VAO 使用的缓冲区。

您的渲染代码接近正确,但不完全正确:

    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);

请注意,第二个 glVertexAttribPointer 调用也使用属性 0。传递给glVertexAttribPointer 的属性索引指定从该数组中获取哪个着色器输入变量。您要做的是使用相同的着色器渲染第二个对象,仅更改顶点数组的来源。这需要使用相同的属性索引。

但是,如果您可以使用 OpenGL 4.3/ARB_vertex_attrib_binding,I would highly encourage you to just use those APIs。它使您的代码更加更简单:

//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);

然后,在你的循环中:

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);

【讨论】:

以上是关于渲染一个包含两个 VBO 的 VAO的主要内容,如果未能解决你的问题,请参考以下文章

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

LWJGL - VBO 和 Sprite 类

在 jogl 中渲染大型 VBO 对象

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

glUseProgram 会改变 VAO 和/或 VBO 状态吗?

在绑定到一个 vao 的 vbo 之间切换