81

我面临一个我认为与 VAO 相关的问题,但我不确定..

我不确定 VAO 的正确用法,我在 GL 初始化期间所做的很简单

glGenVertexArrays(1,&vao)

其次是

glBindVertexArray(vao)

后来,在我的绘图管道中,我只调用了 glBindBuffer()、glVertexAttribPointer()、glEnableVertexAttribArray()等等......而不关心最初绑定的 VAO

这是正确的做法吗?

4

4 回答 4

97

VAO 在绑定方式方面与 VBO 和纹理类似。将单个 VAO 绑定到程序的整个长度不会产生任何性能优势,因为您可能只是在没有 VAO 的情况下进行渲染。事实上,它可能会更慢,具体取决于实现如何在绘制顶点属性设置时截取它们。

VAO 的重点是在初始化期间运行一次绘制对象所需的所有方法,并在主循环期间消除所有额外的方法调用开销。重点是要有多个VAO,并在绘制时在它们之间切换。

就最佳实践而言,以下是组织代码的方式:

initialization:
    for each batch
        generate, store, and bind a VAO
        bind all the buffers needed for a draw call
        unbind the VAO

main loop/whenever you render:
    for each batch
        bind VAO
        glDrawArrays(...); or glDrawElements(...); etc.
    unbind VAO

这避免了绑定/解除绑定缓冲区的混乱,并为每个顶点属性传递所有设置,并用一个方法调用替换它,绑定一个 VAO。

于 2012-01-19T08:53:52.450 回答
28

不,这不是您使用 VAO 的方式。您应该以与使用 VBO 或纹理或着色器相同的方式使用 VAO。首先设置它。并且在渲染过程中只绑定它们,而不修改它。

因此,使用 VAO,您可以执行以下操作:

void Setup() {
    glGenVertexArrays(..);
    glBindVertexArray(..);
    // now setup all your VertexAttribPointers that will be bound to this VAO
   glBindBuffer(..);
   glVertexAttribPointer(..);
   glEnableVertexAttribArray(..);
}

void Render() {
    glBindVertexArray(vao);
    // that's it, now call one of glDraw... functions
    // no need to set up vertex attrib pointers and buffers!
    glDrawXYZ(..)
}

另请参阅以下链接:

于 2012-01-19T08:53:14.777 回答
11

这是正确的做法吗?

是的,这是完全合法和有效的。好吗?出色地...

已经对这类事情进行了一些非正式的性能测试。似乎,至少在经过测试的 NVIDIA 硬件上,VAO 的“正确”使用(即:其他人所提倡的)实际上在许多情况下更慢。如果更改 VAO 不会更改绑定的缓冲区,则尤其如此。

据我所知,没有在 AMD 硬件上进行过类似的性能测试。一般来说,除非它们发生变化,否则这是可以接受的 VAO 使用。

于 2012-01-19T14:39:09.367 回答
3

当我尝试时,罗伯特上面的回答对我有用。值得一提的是在 Go 中使用多个顶点属性对象的代码:

// VAO 1

vao1 := gl.GenVertexArray()
vao1.Bind()

vbo1 := gl.GenBuffer()
vbo1.Bind(gl.ARRAY_BUFFER)

verticies1 := []float32{0, 0, 0, 0, 1, 0, 1, 1, 0}
gl.BufferData(gl.ARRAY_BUFFER, len(verticies1)*4, verticies1, gl.STATIC_DRAW)

pa1 := program.GetAttribLocation("position")
pa1.AttribPointer(3, gl.FLOAT, false, 0, nil)
pa1.EnableArray()
defer pa1.DisableArray()

vao1.Unbind()

// VAO 2

vao2 := gl.GenVertexArray()
vao2.Bind()

vbo2 := gl.GenBuffer()
vbo2.Bind(gl.ARRAY_BUFFER)

verticies2 := []float32{-1, -1, 0, -1, 0, 0, 0, 0, 0}
gl.BufferData(gl.ARRAY_BUFFER, len(verticies2)*4, verticies2, gl.STATIC_DRAW)

pa2 := program.GetAttribLocation("position")
pa2.AttribPointer(3, gl.FLOAT, false, 0, nil)
pa2.EnableArray()
defer pa2.DisableArray()

vao2.Unbind()

然后在你的主循环中,你可以这样使用它们:

for !window.ShouldClose() {
    gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    vao1.Bind()
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    vao1.Unbind()

    vao2.Bind()
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    vao2.Unbind()

    window.SwapBuffers()
    glfw.PollEvents()

    if window.GetKey(glfw.KeyEscape) == glfw.Press {
        window.SetShouldClose(true)
    }
}

如果您想查看完整的源代码,它可以作为 Gist 获得,并且源自 go-gl 中的示例:

https://gist.github.com/mdmarek/0f73890ae2547cdba3a7

感谢大家的原始答案,我和 ECrownofFire 有同样的问题。

于 2014-10-24T12:28:36.633 回答