Para el renderizado de vóxel, ¿qué es más eficiente: VBO prefabricado o un sombreador de geometría?

26

Dada una matriz de vóxel bastante estática, lo que es más eficiente: usar la CPU para pregenerar un VBO para renderizar las caras de vóxel (ignorando formas más avanzadas de renderizado como los cubos de marcha por el momento) o usar un sombreador de geometría en la GPU para generar el rostros sobre la marcha?

No estoy tan preocupado por actualizar los voxels cambiantes, pero por supuesto eso es un beneficio de la versión de GPU ya que no tiene que reconstruir los VBO. Además, el enfoque GS se siente un poco más moderno :)

Por otro lado, no he visto los detalles sobre cómo un GS realmente funciona con la tubería de rasterización en las GPU modernas. ¿Produce los vértices en una especie de caché de flujo o los vértices se escriben en la memoria normal de la GPU en el medio? Si es lo último, supongo que la generación sobre la marcha podría reducir el ancho de banda disponible y la potencia de procesamiento del resto de las tareas de GPU, supongo, y sería más beneficioso hacerlo en la CPU.

Bjorn Wesen
fuente

Respuestas:

9

Estoy pensando en una escena tipo minecraft, donde por voxel te refieres a un mundo de bloques que en realidad se representan usando polígonos:

Si usa un sombreador de geometría, será difícil evitar tener exactamente tres caras (o lo que sea) por vóxel.

Si tiene muchos bloques adyacentes que tienen la misma textura, puede usar el mosaico de las texturas para tener mucho menos triángulos en su tira (degenerada) en un enfoque VBO. Quiero decir, si hay un área grande y plana de 6x6 de vóxeles de hierba, puedes dibujar toda la parte superior en solo 2 triángulos en lugar de 64.

Con el enfoque GS no puede hacer el sacrificio trivial de caras ocluidas por vóxeles adyacentes que es muy sencillo con un enfoque VBO.

No he probado el enfoque GS, pero puedo decir que el enfoque VBO con la combinación de mosaicos adyacentes repetidos funciona muy bien. Descubrí que jugar con los índices de elementos es mucho más lento que simplemente repetir los vértices. Si divide su mundo en bonitos cubos pequeños, generalmente puede usar solo un byte por componente por vértice e incluso empacar la información de textura y las normales (una cara en un cubo alineado con un eje tiene solo 3 posibles normales), etc.en un cuarto byte para hacer 4 bytes por vértice, que es agradable y rápido.

He usado VBO por separado para cada una de las 6 caras: solo es necesario dibujar como máximo 3 de ellas obviamente. Esto encaja muy bien con las diferentes texturas que generalmente se usan en las partes superiores de los vóxeles estilo minecraft. Porque para cada conjunto lo normal y tal es entonces uniforme.

Con el uso de pixmaps en mosaico vertical en un atlas con GL_REPEATel eje horizontal y con versiones rotadas de 90 grados de los pixmaps en el mismo atlas, descubrí que puedo dibujar cantidades masivas de bloques aparentemente diferentes usando el mismo VBO en la misma llamada. En el ejemplo del área de césped 6x6, lo habría dividido en 12 triángulos ya que solo tengo repetir en una dimensión en mi atlas.

Principalmente he estado haciendo que esto funcione en el extremo más bajo de los chips gráficos integrados y dispositivos móviles, donde GS es algo con lo que puedo soñar algún día jugar.

Será
fuente
3
Solo necesita dibujar como máximo 3 caras por vóxel, pero es posible que necesite dibujar diferentes caras para cada vóxel según el punto de vista, por lo que la optimización no es tan fácil, ¿verdad? Un VBO prefabricado contendrá más de un vóxel. Si su punto de vista está entre los vóxeles, verá el lado este de uno y el lado oeste del otro. La única forma en que esto ayudaría es que puede eliminar trivialmente las caras orientadas hacia atrás, pero el peor de los casos es que renderice 5 de 6 lados en un grupo de vóxeles. Si su punto de vista está fuera de los límites axiales del VBO, entonces solo necesita representar 3 lados.
Bjorn Wesen
Spot en Bjorn, es factible. (Pero estoy creando VBO para bloques según sea necesario y reconsiderando lo que he construido cuando la cámara se mueve, en lugar de tener todo el mundo en VBO en todo momento; así que tengo un momento natural para tomar estas decisiones)
Will
10

¿Qué pasa con la tercera opción, usar matrices instanciadas? Básicamente, usted dibuja muchos cuadros (hechos de un simple cubo de 8 vértices) con una sola llamada de dibujo, obteniendo las posiciones (y otros datos) como atributos por instancia del VBO de datos de vóxel (usando glVertexAttribDivisorOpenGL, estoy seguro DX también tiene eso). Esto podría ser más rápido que el enfoque del sombreador de geometría, aunque el código de aplicación (sin sombreador) debería ser bastante similar, ya que recuerdo que los sombreadores de geometría tienen una reputación de ser lentos, aunque no tengo experiencia con ellos (o instancias) ya que todavía me siento en hardware 2.1.

Pero de todos modos, los sombreadores de geometría o los arreglos instanciados deberían ser más adecuados que la geometría de vóxel construida por la CPU, especialmente cuando los datos de vóxel están sujetos a cambios. Junto con la retroalimentación de transformación (¿salida de flujo en DX?), Es posible que pueda configurar una buena técnica de eliminación basada en GPU.

Chris dice reinstalar a Mónica
fuente
Sí, esta es la mejor solución a este problema. ¿Por qué no se me ocurrió? :)
Notabene
Después de experimentar un poco, debo decirle que la geometría horneada supera cualquier instancia por un amplio margen. Sin embargo, todavía no he probado los sombreadores de geometría.
Jari Komppa
@JariKomppa, ¿puedes explicar qué quieres decir con geometría horneada?
Steven Lu
Instancias pretraducidas y copiadas a una sola malla. Como tener una malla que representa cien cubos o lo que sea.
Jari Komppa
@JariKomppa He visto los mismos resultados, donde crear la malla es mucho más rápido. Sin embargo, en el gtx 680, la opción de instancia parece funcionar mucho más rápido, raro.
Levi H
1

La versión del sombreador de geometría me suena mucho mejor. Solo puede tener el punto vbo y el cuadro de construcción sobre la marcha (punto de entrada, flujo de triángulo de salida). Será rápido (incluso más rápido si va a usar la unidad de teselación en el modelo de sombreador 5 eq. DX11) y reducirá el ancho de banda extremadamente, será una solución agradable y limpia.

Sobre GS Se coloca entre el sombreador de vértices y el sombreador de píxeles y modifica el flujo de vértices (primitivos) salidos. Mientras que el sombreador de vértices funciona solo en los vértices, el sombreador de geometría funciona en primitivas enteras. La salida de esta secuencia solo va al sombreador de píxeles (y se rasteriza antes de eso fuera de curso :)) y no hay forma de guardarla. (Tal vez por un renderizado loco a la textura y luego analizarlo ... pero no existe una posibilidad real simple)

Nota de rendimiento: debe poder hacer todo en el sombreador de geometría y omitir (solo pasar datos) el sombreador de vértices. Pero no es la mejor manera. Mejor (más rápido) es hacer la mayor parte de la transformación posible en el sombreador de vértices e intentar minimizar el programa de sombreador de geometría. No tenga miedo de usarlo para el ciclo si lo necesita (por ejemplo, para la creación de cajas). El compilador lo desenrollará por usted.

Notabene
fuente
2
Puede ser una buena idea verificar los vóxeles adyacentes en la geometría y / o el sombreador de vértices y descartar los vértices u omitir las caras si están ocluidas. De lo contrario, la solución GS aumentará el ancho de banda utilizado.
Tamschi
El ancho de banda no será un gran problema (según mis experiencias), pero por supuesto es cierto. Y no puede buscar en las otras primitivas en GS (es lo sé :)).
Notabene
@Tamschi: sí, este problema se me ocurrió justo después de escribir esta pregunta ... para la versión de CPU, se suprimen los vóxeles en el medio de los sólidos, pero esto podría ser imposible en la GPU sin un pase previo con lo que equivaldría a un diferenciando ..
Bjorn Wesen
1
Puede vincular el búfer de vértices a un uniforme isamplerBuffer o usamplerBuffer en el sombreador, luego realizar búsquedas con textura (nombre_de_uniforme, índice). Otra opción sería unir el búfer a una matriz uniforme, lo que le da más libertad en el formato de vértice que desea usar.
Tamschi