Mejores prácticas de OpenGL VAO

79

Estoy enfrentando un problema que creo que depende de VAO, pero no estoy seguro.

No estoy seguro del uso correcto de un VAO, lo que solía hacer durante la inicialización de GL era un simple

glGenVertexArrays(1,&vao)

seguido de un

glBindVertexArray(vao)

y más tarde, en mi canal de dibujo, llamé glBindBuffer (), glVertexAttribPointer (), glEnableVertexAttribArray () y así sucesivamente ... sin importarme el VAO inicialmente vinculado

¿Es esta una práctica correcta?

user815129
fuente
31
No estoy seguro de por qué esta pregunta obtuvo un -1 .. no muestra ningún esfuerzo y. O no está claro? Pasé 3 días luchando con las especificaciones, wikis y foros antes de publicar esto y no obtuve ninguna razón real por la que debería usar VAO .. meh ..
user815129
18
Porque para los hermanos geniales, todo es obvio tan pronto como tienen sus cortes y pegados a mano, ¿por qué molestarse en comprender las cosas?
mlvljr

Respuestas:

93

Los VAO actúan de manera similar a los VBO y las texturas con respecto a cómo se unen. Tener un solo VAO limitado para toda la duración de su programa no producirá beneficios de rendimiento porque también podría estar renderizando sin VAO en absoluto. De hecho, puede ser más lento dependiendo de cómo la implementación intercepta la configuración de los atributos de vértice a medida que se dibujan.

El objetivo de un VAO es ejecutar todos los métodos necesarios para dibujar un objeto una vez durante la inicialización y eliminar toda la sobrecarga adicional de llamadas a métodos durante el ciclo principal. El punto es tener varios VAO y cambiar entre ellos al dibujar.

En términos de mejores prácticas, así es como debe organizar su código:

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

Esto evita el lío de vincular / desvincular búferes y pasar todas las configuraciones para cada atributo de vértice y lo reemplaza con una sola llamada al método, vinculando un VAO.

Robert Rouhani
fuente
1
¿Es realmente necesario desvincular la VAO? He encontrado diferentes prácticas alrededor
user815129
3
No es necesario, pero generalmente lo hago en caso de que algún otro objeto decida renderizar sin VAO (depuración de dibujo / biblioteca de renderización de fuentes / etc.), ya que eso (hasta donde yo sé) reemplazaría la configuración de atributos para el VAO actualmente vinculado .
Robert Rouhani
2
¿Por qué la desvinculación ocurre solo una vez y fuera del ciclo? ¿No debería ocurrir la desvinculación por lotes?
batbrat
12
La vinculación de un nuevo VAO reemplaza al antiguo VAO. Desenlazar dentro del bucle sería un trabajo extra.
Robert Rouhani
1
@RobertRouhani ¿Hay algún beneficio en ese último VAO desvinculado, entonces? (¿La desvinculación no afecta nada?)
Mateen Ulhaq
27

No, no es así como se usa VAO. Debe usar VAO de la misma manera que usa VBO o texturas o sombreadores. Primero configúrelo. Y durante el renderizado solo enlazarlos, sin modificarlo.

Entonces con VAO haces lo siguiente:

Vea también estos enlaces:

Mārtiņš Možeiko
fuente
1
Esta respuesta debería ser más alta. Específicamente solucionó un error mío difícil de rastrear.
abarax
3
glEnableVertexAttribArray(...)debe llamarse antes glVertexAttribPointer(...). A algunos conductores (incluido el mío) realmente no les gusta lo contrario.
RecursiveExceptionException
2
Se votó a favor porque la mayoría de los ejemplos no están claros de que TIENE que llamar a glEnableVertexAttribArray durante el enlace de VAO.
Rupert Rawnsley
10

¿Es esta una práctica correcta?

Sí, esto es perfectamente legal y válido. ¿Esta bien? Bien...

Ha habido algunas pruebas de rendimiento informales sobre este tipo de cosas. Y parece que, al menos en el hardware NVIDIA donde se probó esto, el uso "adecuado" de los VAO (es decir, lo que todos los demás defendían) en realidad es más lento en muchos casos. Esto es especialmente cierto si el cambio de VAO no cambia qué búferes están vinculados.

Hasta donde yo sé, no se han realizado pruebas de rendimiento similares en hardware AMD. En general, a menos que algo cambie con ellos, este es un uso aceptable de los VAO.

Nicol Bolas
fuente
2
Seguramente, una vez que cada VAO tenga suficientes estados diferentes para rastrear, cambiar manualmente los búferes y los ptrs de atributo durante el ciclo de renderizado requeriría muchas llamadas, ¿comenzaríamos a ver un rendimiento mejorado del uso de VAO?
Steven Lu
Desafortunadamente, el vínculo está muerto.
Nick Caplinger
3

La respuesta de Robert anterior funcionó para mí cuando la probé. Por lo que vale aquí es el código, en Go, de usar múltiples objetos de atributo de vértice:

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

Luego, en su bucle principal, puede usarlos como tales:

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

Si desea ver la fuente completa, está disponible como Gist y se deriva de los ejemplos en go-gl:

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

Gracias a todos por las respuestas originales, tenía la misma pregunta que ECrownofFire.

Marek
fuente
3
No es necesario desvincular vao1 antes de vincular vao2, basta con vincular vao2.
Havokentity