最佳实践

我面临一个问题,我相信是 VAO 依赖,但我不确定..。

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

glGenVertexArrays(1,&vao)

然后是

glBindVertexArray(vao)

后来,在我的绘图流水线中,我只是调用 GlBindBuffer () ,glVertexAttribPointer () ,glEnableVertexAttribArray ()等等... 而不关心初始绑定的 VAO

这是正确的做法吗?

47120 次浏览

不,那不是你使用 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(..)
}

请参阅以下链接:

VAOs 在如何约束方面的作用类似于 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 的方法调用来替换它。

这是正确的做法吗?

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

在这种事情上有一些 非正式的性能测试。而且看起来,至少在测试它的 NVIDIA 硬件上,VAOs 的“正确”使用(即: 其他人所提倡的)在许多情况下实际上是 慢一点。如果更改 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。