¿Cómo puedo generar campos de distancia con signo (2D) en tiempo real, rápido?

21

En una pregunta anterior , se sugirió que los campos de distancia firmados pueden calcularse previamente, cargarse en tiempo de ejecución y luego usarse desde allí.

Por razones que explicaré al final de esta pregunta (para las personas interesadas), necesito crear los campos de distancia en tiempo real.

Hay algunos documentos disponibles para diferentes métodos que se supone que son viables en entornos en tiempo real, como los métodos para transformaciones de distancia de Chamfer y transformaciones basadas en aproximación de diagrama de Voronoi (como se sugiere en esta presentación por el desarrollador de Pixeljunk Shooter ), pero A mí (y, por lo tanto, se puede suponer que muchas otras personas) me cuesta mucho ponerlos en práctica, ya que generalmente son largos, en gran parte hinchados con las matemáticas y no son muy algorítmicos en su explicación.

¿Qué algoritmo sugeriría para crear los campos de distancia en tiempo real (favorablemente en la GPU) especialmente teniendo en cuenta la calidad resultante de los campos de distancia?

Como estoy buscando una explicación / tutorial real en lugar de un enlace a otro documento o diapositiva, esta pregunta recibirá una recompensa una vez que sea elegible para uno :-).

He aquí por qué necesito hacerlo en tiempo real:

Si tiene que calcular previamente estas SDF para entornos 2D grandes (piense en un mapa grande similar a Terraria), esto significaría que está aceptando una sobrecarga bastante grande en el espacio de almacenamiento (y el tiempo de generación del mapa) a favor de implementar un algoritmo complicado que es lo suficientemente rápido para la generación de SDF en tiempo real.

Por ejemplo, un mapa relativamente pequeño con 1000 * 256 (ancho * alto) con un tamaño de mosaico de 10 * 10 píxeles y, por lo tanto, dimensiones totales de 10000 * 2560 píxeles ya le costaría alrededor de 2 megabytes de tamaño, si elige un tamaño relativamente pequeño Resolución SDF de 128x128, suponiendo que está almacenando solo los valores de distancia de 0 a 255.

Obviamente, esto puede convertirse rápidamente en demasiado y es una sobrecarga que no quiero tener.

Hay algo mas:

Las SDF se pueden usar para muchas cosas (como la detección de colisiones), y algunas aplicaciones útiles aún no se han descubierto. Creo que mucha gente buscará estas cosas en el futuro, y si obtenemos una respuesta integral aquí, creo que ayudaremos a mucha gente.

TravisG
fuente
Busqué en Google "qué es un campo de distancia firmado" y el primer éxito fue una versión de GPU: http.developer.nvidia.com/GPUGems3/gpugems3_ch34.html Es un poco viejo, pero podría ayudar a sus búsquedas.
Patrick Hughes
1
Tal vez me falta algo, pero estoy algo confundido por la afirmación de por qué necesita hacerlo en tiempo real (y no menos importante, por qué está etiquetado con spoiler); en primer lugar, ¿de dónde sacas la cifra de 2 MB para un SDF de 128x128? En segundo lugar, ¿por qué considera que 2MB es un uso de memoria particularmente pesado? Estoy de acuerdo en que no es insustancial, pero parece una pequeña fracción del uso general de la memoria del mapa. Y tercero, ¿cómo generará el campo en tiempo real para salvar esa memoria? Todavía necesita almacenar exactamente los mismos datos, ya sea que se generen sobre la marcha o precalculados, ¿no?
Steven Stadnicki
En términos más generales, podría ser fácilmente que los SDF no sean la técnica que necesita. Más información sobre su situación específica (recuento estático de obstáculos, recuento dinámico de obstáculos, etc.) y, precisamente, qué efecto espera lograr sería útil para tratar de precisar lo que es más probable que sea útil para usted.
Steven Stadnicki
1
Si generara el campo de distancia en tiempo real, solo crearía esos 2 MB una vez por cuadro (la sobrecarga de memoria total siempre sería la memoria necesaria para un campo de distancia, ya que solo necesito el de la pantalla). Si tengo un mapa más grande que mi ejemplo de 1000x128 (creo que los mapas grandes de Terraria van mucho más allá de los 10000), necesito uno de esos 2mb por cada submapa de 1000x128 de ese mapa. En primer lugar, la razón por la que necesito SDF se describe en la primera pregunta que vinculé al principio de esta pregunta (es para la proyección de sombras 2D de GPU).
TravisG
1
@heishe, ¿estás tratando de generar 2Mb de datos una vez por fotograma? ¿Seriamente?
kaoD

Respuestas:

9

Catalin Zima explica cómo lograr sombras dinámicas en 2D en su artículo , y usa un campo de distancia con signo (por lo que puedo decir, es solo un nombre elegante para un búfer de sombra en este contexto). Su método necesita una GPU, y su implementación tal cual no es la mejor (cayó por debajo de 60Hz a aproximadamente 20 luces en mi máquina, la mía obtuvo aproximadamente 500 luces); lo cual es de esperarse ya que ha favorecido la claridad del código sobre la velocidad.

Implementación

Exactamente como lo implementó él:

  1. Renderiza todas las ruedas de sombra en una textura.
  2. Calcule la distancia al centro de la luz para cada píxel y asigne ese valor al RGB de píxeles opacos.
  3. Distorsiona la imagen para que represente cómo una cámara 3D habría visto esos píxeles.
  4. Comprima la imagen en una imagen de tamaño 2xN usando el cambio de tamaño inusual descrito en su artículo (un cambio de tamaño simple no funcionará).
  5. La imagen de 2xN es ahora su campo de distancia firmado para los cuatro cuadrantes de la luz (recuerde que un cuadrante es básicamente un tronco de cámara única a 90 grados).
  6. Renderiza el mapa de luz.
  7. Desenfoque el mapa de luz (basado en la distancia desde la luz) para obtener sombras suaves.

Mi implementación final fue (cada paso es un solo sombreador):

  1. Hacer (1).
  2. Haz (2) y (3) .
  3. Hacer (4). Su implementación es realmente lenta: si puede intentar usar GPGPU para esto. No pude usar GPGPU (XNA), así que lo que hice fue:
    • Establezca una malla donde las primeras columnas N / 2 fueron representadas por N / 2 quads con la misma posición EXACT (cubriendo la primera columna del buffer final) pero con diferentes coordenadas de textura (lo mismo para las segundas columnas N / 2)
    • Desactiva las pruebas de profundidad en la GPU.
    • Use la función de combinación de píxeles MIN.
  4. Haz (6) y (7).

Es bastante ingenioso: es básicamente una traducción directa de cómo se manejan las sombras en 3D a 2D.

Trampas

El principal inconveniente es que algunos objetos no deberían sombrearse: en mi ejemplo, estaba escribiendo un clon de Liero (gusanos en tiempo real) y, por lo tanto, no quería que, por ejemplo, los gusanos de los jugadores fueran sombreados (al menos el en la pantalla de cada jugador). Todo lo que hice por estos objetos 'especiales' fue volver a dibujarlos como último paso. La ironía era que la mayoría de los objetos no estaban sombreados (gusanos, primer plano del paisaje), por lo que aquí hay un problema de sobregiro.

Jonathan Dickinson
fuente
¿Fue su ajuste al método de cambio de tamaño lo único que lo aceleró para manejar 500 luces por encima de 60 fps?
TravisG
OK, aceptaré la respuesta ya que resuelve mi problema original, pero en realidad no responde por lo que di la recompensa. Esperaré y tal vez alguien se demore en explicar uno de los varios métodos O (N) para la generación de campos de distancia con signo.
TravisG
@heishe con respecto a su primera pregunta: no estoy seguro. Hice todas las optimizaciones de una sola vez. Creo que recuerdo haberlo apagado y ver que la velocidad de fotogramas disminuyó sustancialmente. En total, 6 llamadas de sorteo por luz matarán tu velocidad de cuadros. Como dije, parece, por lo que puedo decir, tienes 4 campos de distancia firmados en el paso (5), pero alguien que sepa más sobre ellos deberá confirmarlo.
Jonathan Dickinson el
Bueno, es un caso muy especial de un campo de distancia firmado. En un campo de distancia con signo normal, cada píxel contiene la distancia al obstáculo más cercano. En este algoritmo, el campo de distancia solo contiene un obstáculo, y también el obstáculo es de solo 1 píxel en toda la imagen (la fuente de luz), por lo que este campo de distancia se puede generar en O (N).
TravisG
1
@heishe aquí está mi sombreador: gist.github.com/2384073 . DistortPS es 2 + 3.
Jonathan Dickinson el