OpenGL VAO best practices

2020-01-27 09:52发布

问题:

Im facing an issue which I believe to be VAO-dependant, but Im not sure..

I am not sure about the correct usage of a VAO, what I used to do during GL initialization was a simple

glGenVertexArrays(1,&vao)

followed by a

glBindVertexArray(vao)

and later, in my drawing pipeline, I just called glBindBuffer(), glVertexAttribPointer(), glEnableVertexAttribArray() and so on.. without caring about the initally bound VAO

is this a correct practice?

回答1:

VAOs act similarly to VBOs and textures with regard to how they are bound. Having a single VAO bound for the entire length of your program will yield no performance benefits because you might as well just be rendering without VAOs at all. In fact it may be slower depending on how the implementation intercepts vertex attribute settings as they're being drawn.

The point of a VAO is to run all the methods necessary to draw an object once during initialization and cut out all the extra method call overhead during the main loop. The point is to have multiple VAOs and switch between them when drawing.

In terms of best practice, here's how you should organize your code:

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

This avoids the mess of binding/unbinding buffers and passing all the settings for each vertex attribute and replaces it with just a single method call, binding a VAO.



回答2:

No, that's not how you use VAO. You should use VAO in same way how you are using VBO or textures, or shaders. First set it up. And during rendering only Bind them, without modifying it.

So with VAO you do following:

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(..)
}

See also these links:

  • http://www.swiftless.com/tutorials/opengl4/4-opengl-4-vao.html
  • http://www.lastrayofhope.com/2011/07/30/using-vertex-array-objects/


回答3:

is this a correct practice?

Yes, this is perfectly legal and valid. Is it good? Well...

There has been some informal performance testing on this sort of thing. And it seems, at least on NVIDIA hardware where this was tested, the "proper" use of VAOs (ie: what everyone else advocated) actually is slower in many cases. This is especially true if changing VAOs does not change which buffers are bound.

No similar performance testing has taken place on AMD hardware, to my knowledge. In general, unless something changes with them, this is an acceptable use of VAOs.



回答4:

Robert's answer above worked for me when I tried it. For what it's worth here is the code, in Go, of using multiple 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()

Then in your main loop you can use them as such:

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

If you want to see the full source, it is available as a Gist and derived from the examples in go-gl:

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

Thanks everyone for the original answers, I had the same question as ECrownofFire.