OpenGL VAO最佳做法


79

我面临的问题是我认为取决于VAO,但我不确定。

我不确定VAO的正确用法,我在GL初始化过程中曾经做过的事情很简单

glGenVertexArrays(1,&vao)

跟一个

glBindVertexArray(vao)

之后,在我的绘制管道中,我只调用了glBindBuffer(),glVertexAttribPointer(),glEnableVertexAttribArray()等。

这是正确的做法吗?


31
我不知道为什么这个问题得-1 ..它没有显示出任何努力和/或不清楚?在发布此消息之前,我花了3天的时间与规范,Wiki和论坛进行斗争,并没有得到我应该使用VAO的任何真正原因
。.–

18
因为对于酷兄弟来说,只要他们方便地进行剪切和粘贴,这一切都是显而易见的,为什么还要花时间去理解东西呢?
mlvljr

Answers:


93

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)将其替换。


1
解除VAO绑定真的有必要吗?Ive在周围发现了不同的做法
user815129'1

3
并不是必须的,但是我通常会这样做,以防万一其他对象决定不使用VAO进行渲染(调试图形/字体渲染库/等),因为据我所知,它将替换当前绑定的VAO的属性设置。
罗伯特·鲁哈尼

2
为什么取消绑定仅发生一次并在循环之外发生?解除绑定是否不应该按批次进行?
batbrat

12
绑定新的VAO将替换旧的VAO。在循环内部解除绑定将只是额外的工作。
罗伯特·鲁哈尼

1
@RobertRouhani那么最后一个解除绑定的VAO有什么好处?(不绑定不影响任何东西吗?)
Mateen Ulhaq '19

27

不,那不是您使用VAO的方式。您应该以与使用VBO或纹理或着色器相同的方式使用VAO。首先进行设置。并且仅在渲染期间绑定它们,而无需对其进行修改。

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

另请参阅以下链接:


1
这个答案应该更高。它专门修复了难以追踪的错误。
abarax 2014年

3
glEnableVertexAttribArray(...)应该在之前被调用glVertexAttribPointer(...)。相反,有些司机(包括我的)真的不喜欢它。
RecursiveExceptionException

2
由于大多数示例尚不清楚您是否在VAO绑定期间必须调用glEnableVertexAttribArray而提出了建议。
罗珀特·罗恩斯利

10

这是正确的做法吗?

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

已经有一些非正式的性能测试。而且,至少在经过测试的NVIDIA硬件上,似乎在许多情况下,“正确”使用VAO(即其他所有人所倡导的)的速度实际上较慢。如果更改VAO不会更改绑定的缓冲区,则尤其如此。

据我所知,尚未在AMD硬件上进行过类似的性能测试。通常,除非它们有所更改,否则可以接受VAO。


2
当然,一旦每个VAO具有足够可跟踪的各种状态,那么在渲染循环中手动切换缓冲区和属性指针将需要进行多次调用,那么使用VAO可以使我们看到性能的提高吗?
史蒂文·卢

不幸的是,链接已死。
尼克·卡普林格

3

当我尝试时,以上罗伯特的回答对我有用。在这里值得使用Go中使用多个Vertex Attribute Objects的代码:

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

如果要查看完整的源代码,可以将其作为要点,并从go-gl中的示例获取:

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

谢谢大家的原始回答,我和ECrownofFire有相同的问题。


3
您无需在绑定vao2之前取消绑定vao1,只需绑定vao2就足够了。
havokentity,2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.