¿Es posible construir un cubo con menos de 24 vértices?

14

Tengo un mundo basado en cubos como Minecraft y me pregunto si hay una manera de construir un cubo con menos de 24 vértices para poder reducir el uso de memoria.

No me parece posible por 2 razones: las normales no saldrían bien y las texturas per-face no funcionarían.

¿Es este el caso o estoy equivocado? ¿Quizás haya alguna nueva tecnología elegante de DX11 que pueda ayudar?

Editar: solo para aclarar, tengo 2 requisitos: necesito normales de superficie para cada cara de cubo para hacer una iluminación adecuada y necesito una forma de abordar un índice diferente en una matriz de textura para cada cara de cubo

Telanor
fuente
1
¿Desea reducir el uso de memoria de la tarjeta gráfica o el uso de RAM ?
doppelgreener
1
Los datos de vértice se almacenan en la RAM y en la GPU en este momento, por lo que reducirlos reduciría ambos.
Telanor

Respuestas:

9

Si solo necesita normales por cara, y si sus texcoords para una cara son estrictamente 0/0, 0/1, 1/0, 1/1 (o similar para adaptarse a su diseño), entonces puede construir un cubo con 8 verts y 30 (tira con reinicio) o 36 (lista) índices. Obtenga las normales y los códigos de texto utilizando una búsqueda de matriz constante basada en SV_VertexID en su sombreador de vértices.

Hacer esto significa que ni siquiera necesita incluir texcoords o normales en su búfer de vértices, lo que le dará aún más ahorro de memoria.

Yendo más lejos, aún podría llegar a 24 verts por cubo, pero también usar instancing. Cada cubo tendría un tamaño fijo en su búfer de vértices (1x1x1) y tendría un factor de escala y una posición (suponiendo que sus cubos no giren, una matriz si lo hacen) como datos por instancia. En el caso no rotativo, tiene un costo único de 24 verts, pero luego cada cubo solo necesita 6 flotadores para especificar por completo. En el caso de rotación, está viendo 16 flotadores, pero incluso eso es un ahorro sustancial (es más probable que se produzca un cuello de botella en las transformaciones de la matriz en este caso, para el caso no giratorio que construye una matriz sobre la marcha) su sombreador de vértices, incluso si está hecho por vértice, es tan estúpidamente rápido que ni siquiera necesita preocuparse por eso).

Para texturas por cara, solo use una matriz de texturas. Debe asegurarse de que cada una de esas texturas en la matriz sea del mismo tamaño, por supuesto, y aún tendrá que dividir su lote actual si la matriz en sí misma necesita cambiar, pero de lo contrario hará el trabajo bien. Agregue un tercer texcoord a su definición de vértice que define el segmento de matriz que se utilizará para cada cara.

No necesita un GS con esto, y debe ejecutarse más rápido que usar uno, ya que tener habilitada la etapa de sombreado de geometría impondrá una sobrecarga adicional.

Tengo un código de referencia en mi motor que solo dibuja un montón de cubos usando este método, y puedo masticar fácilmente más de 300,000 cubos sin dejar de limpiar 60 fps, en una GPU relativamente baja, y sin hacer nada más para optimizar el proceso . Es cierto que no los estoy iluminando ni texturizando, pero tengo habilitada la mezcla alfa, desactivado el descarte de la cara posterior y, en general, se equilibra con mi parte de "no hacer nada más para optimizar", por lo que debería darle una idea razonable del tipo de estadio puede golpear con este método.

Maximus Minimus
fuente
1
Estaba usando instancias antes y funciona muy bien para menos de 40k cubos. Pero tengo al menos 256k cubos, y las instancias tampoco lo manejaban. Sin embargo, usar SV_VertexID con una búsqueda suena muy prometedor.
Telanor
¿Qué tan grande era su búfer por instancia? Intentar meterlos a todos en un enorme búfer puede terminar asfixiando tu GPU; Generalmente mantengo las memorias intermedias por instancia en suficiente espacio para 64k objetos (dividiendo las llamadas de dibujo si es necesario), uso el patrón de descarte / no sobrescribir y escribo directamente en el puntero mapeado en lugar de copiar desde una matriz intermedia, que funciona muy bien .
Maximus Minimus
Además, mantener bajo el número de mapas es esencial. Map / write one cube / unmap 40k times va a ser mucho más lento que map / write 40k cubes / unmap solo una vez. Si estaba haciendo lo primero, intente cambiar a lo último.
Maximus Minimus
15

Creo que la optimización principal que puede hacer se basa en el hecho de que no todos los cubos necesitarán los 24 vértices . De hecho, los únicos cubos que necesitan 24 vértices son los que están flotando en el aire, lo que probablemente es una ocurrencia rara.

En general, solo genere quads para las caras que están en contacto con el aire . Esto significa que si dos cubos se tocan, no necesita generar vértices para la cara donde se tocan.

La imagen a continuación muestra este concepto, pero en 2D para una comprensión más fácil. En la imagen hay 11 bloques ocupados (representados por los círculos grises rellenos), que normalmente requerirían 4 x 11 = 44 bordes para representar (4 porque es un cuadrado, no un cubo). Pero como puede ver, solo necesita dibujar los bordes cuando están en contacto con un cuadrado vacío, que en este caso, solo tiene 8 bordes.

ingrese la descripción de la imagen aquí

Si un ejemplo tan simple en 2D logró reducir 44 bordes a 8 bordes, imagine las ganancias en un gran mundo 3D ...

Este es el enfoque descrito en este artículo , que le recomiendo que lea, a pesar de estar dirigido a OpenGL. Sin embargo, los conceptos deberían ser bastante universales.

Probablemente también podría usar un sombreador de geometría para generar los vértices sobre la marcha en la GPU, eliminando la necesidad de almacenarlos en la memoria, pero no tengo experiencia con eso, ni sé qué tan bien funcionaría para un gran mundo.

David Gouveia
fuente
1
Artículo muy interesante Ya estoy haciendo la optimización de borde, lo que sin duda ayuda mucho. Voy a probar los sombreadores de geometría y ver si eso funciona.
Telanor
¿Quiere decir que usa el sombreador de geometría y el producto cruzado para calcular las normales? Estaba pensando en usar eso, como crear un programa separado para superficies planas (no me interesan las texturas).
Dreta
@dreta No solo eso, sino que va al punto donde solo envía los centroides del cubo como una lista de puntos, y el GS emite todos los vértices. Quizás codifique información vecina en los puntos para que el sombreador solo genere las caras que realmente se necesitan, aunque no he pensado cómo.
David Gouveia
Si lo que quieres es rendimiento, no uses un sombreador de geometría ... Casi nunca. Ellos completamente arruinan la tubería. Si desea generar geometría en la GPU, use un sombreador de cómputo o openCL
Robert Fraser