La forma más eficiente de dibujar una gran cantidad de los mismos objetos, pero con diferentes transformaciones

8

Me gustaría dibujar un gran número (múltiples miles) de mallas simples (cada una quizás ... un máximo de 50 triángulos, pero incluso ese es un límite superior muy grande), todos los cuales son exactamente iguales.

Como una simple forma de fuerza bruta, en este momento solo estoy haciendo miles de llamadas de extracción, donde solo cambio las matrices de transformación que necesitan los sombreadores y luego vuelvo a dibujar los mismos objetos.

Por supuesto, esto es terriblemente lento, ya que (supongo) hay demasiadas llamadas de extracción para que el controlador y mi PC se manejen de manera eficiente.

¿Qué puedo hacer para hacerlo más rápido?

TravisG
fuente
44
No puedo darle una respuesta completa, pero la respuesta de alto nivel es usar instancias de geometría .
Seth Battin
Busque en Google "Geometry Instancing", tal vez pueda encontrar algo que lo ayude de esa manera
Luis W

Respuestas:

11

La solución a esto es la instancia. Este tutorial explica algunos métodos de creación de instancias. Si tienes ARB_draw_instancedy ARB_instanced_arrays, úsalos.

La idea general es almacenar todas las transformaciones de sus mallas en un objeto de búfer separado y vincularlo a una matriz de atributos que utiliza un "vértice" por instancia a través de glVertexAttribDivisor.

Si, después de eso, todavía no está corriendo tan rápido como lo desea, probablemente esté vinculado al procesador de vértices. Haga el sacrificio de frustum en las mallas, y preferiblemente construya un BVH para atravesar en lugar de hacer el sacrificio en todas las mallas de forma independiente.

Robert Rouhani
fuente
1
+1; con muchos miles de objetos, también puede unirse a la CPU en las transformaciones (solo puede enviar la posición y la rotación en el búfer por instancia y calcular la matriz por objeto en la GPU), y no olvide ver cómo ¡actualizas ese búfer de vértices dinámico!
Maximus Minimus
@Robert Rouhani: Gracias, lo investigué y lo implementé rápidamente. Ahora puedo dibujar 5000 mallas con 20 tris cada una a 250 FPS. Por cierto, su forma sugerida parece un poco anticuada. Como OpenGL 3.algo de instancias de geometría es parte oficial de la especificación central de OpenGL, ya no es necesario utilizar ARB_xxx. Utilizo un búfer uniforme para almacenar las matrices y uso glDrawElementsInstanced. Dentro del sombreador de vértices, hay un diseño (std140) matrices uniformes {matriz mat4 [5000]; } donde puedo acceder al elemento relevante a través de la matriz [gl_InstanceID]
TravisG
1
@TravisG Tiendo a tratar de admitir versiones anteriores. Entiendo que ambas extensiones han sido promovidas a núcleo por un tiempo, la cadena de extensión aún estará presente, sin embargo :). Además, el uso glVertexAttribDivisor(que no está desaprobado ni desactualizado de ninguna manera) tiene el beneficio de no tener un límite superior en el recuento de instancias. El rendimiento ha sido históricamente mejor que los amortiguadores uniformes, pero ahora es probablemente el mismo o muy similar, y en ambos sentidos se hace el trabajo.
Robert Rouhani
@Robert Rouhani: Mhm ... supongo que es verdad. Alguien con controladores desactualizados podría admitir ARB_xxx pero no las funciones principales oficiales de OpenGl 3.x. Echaré un vistazo a glVertexAttribDivisor, gracias.
TravisG
@Robert Rouhani: implementé ambas versiones (usando buffers uniformes, y alternativamente su versión sugerida donde uso un atributo de vértice que solo cambia una vez por instancia), y la versión de buffer uniforme es en realidad aproximadamente dos veces más rápida (4000 fps en modo de lanzamiento, vs ~ 2000 con glVertexAttribDivisor, el número de 250 fps fue solo para el lado de la CPU).
TravisG