我面临一个我认为与 VAO 相关的问题,但我不确定..
我不确定 VAO 的正确用法,我在 GL 初始化期间所做的很简单
glGenVertexArrays(1,&vao)
其次是
glBindVertexArray(vao)
后来,在我的绘图管道中,我只调用了 glBindBuffer()、glVertexAttribPointer()、glEnableVertexAttribArray()等等......而不关心最初绑定的 VAO
这是正确的做法吗?
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。
不,这不是您使用 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(..)
}
另请参阅以下链接:
这是正确的做法吗?
是的,这是完全合法和有效的。好吗?出色地...
已经对这类事情进行了一些非正式的性能测试。似乎,至少在经过测试的 NVIDIA 硬件上,VAO 的“正确”使用(即:其他人所提倡的)实际上在许多情况下更慢。如果更改 VAO 不会更改绑定的缓冲区,则尤其如此。
据我所知,没有在 AMD 硬件上进行过类似的性能测试。一般来说,除非它们发生变化,否则这是可以接受的 VAO 使用。
当我尝试时,罗伯特上面的回答对我有用。值得一提的是在 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 有同样的问题。