Come posso generare campi a distanza segnati (2D) in tempo reale, velocemente?


21

In una domanda precedente , è stato suggerito che i campi della distanza firmati possono essere pre-calcolati, caricati in fase di esecuzione e quindi utilizzati da lì.

Per ragioni che spiegherò alla fine di questa domanda (per le persone interessate), devo creare i campi di distanza in tempo reale.

Ci sono alcuni documenti là fuori per diversi metodi che dovrebbero essere praticabili in ambienti in tempo reale, come i metodi per le trasformazioni della distanza di smusso e le trasformazioni basate sull'approssimazione del diagramma Voronoi (come suggerito in questa presentazione dal ragazzo dev Pixeljunk Shooter ), ma Io (e quindi si può presumere che molte altre persone) stiano davvero facendo fatica a metterli in pratica, dato che di solito sono lunghi, in gran parte gonfiati dalla matematica e non molto algoritmici nella loro spiegazione.

Quale algoritmo suggeriresti per creare i campi di distanza in tempo reale (favorevolmente sulla GPU) soprattutto considerando la qualità risultante dei campi di distanza?

Dal momento che sto cercando una spiegazione / tutorial reale invece di un collegamento a un altro documento o diapositiva, questa domanda riceverà una ricompensa una volta che ne ha diritto a uno :-).

Ecco perché devo farlo in tempo reale:

Se devi precomputare questi SDF per grandi ambienti 2D (pensa a una grande mappa simile a Terraria), ciò significherebbe che stai accettando un sovraccarico piuttosto grande nello spazio di archiviazione (e tempo di generazione della mappa) a favore dell'implementazione di un altro algoritmo complicato che è abbastanza veloce per la generazione di SDF in tempo reale.

Ad esempio, una mappa relativamente piccola con 1000 * 256 (larghezza * altezza) con una dimensione della piastrella di 10 * 10 pixel e quindi dimensioni totali di 10000 * 2560 pixel ti costerebbe già circa 2 megabyte di dimensione, se scegli una dimensione relativamente piccola Risoluzione SDF di 128x128, supponendo che si stiano memorizzando solo i valori di distanza da 0 a 255.

Ovviamente, questo può rapidamente diventare troppo ed è un sovraccarico che non voglio avere.

C'è qualcos'altro:

Gli SDF possono essere utilizzati per molte cose (come il rilevamento delle collisioni) e alcune utili applicazioni potrebbero non essere ancora ancora scoperte. Penso che molte persone cercheranno queste cose in futuro, e se avremo una risposta completa qui, penso che aiuteremo molte persone.


Ho cercato su google "cos'è un campo di distanza con segno" e il primo colpo è stato una versione GPU: http.developer.nvidia.com/GPUGems3/gpugems3_ch34.html È un po 'vecchio ma potrebbe aiutare ulteriormente le tue ricerche.
Patrick Hughes,

1
Forse mi manca qualcosa, ma sono un po 'confuso dall'affermazione del motivo per cui è necessario farlo in tempo reale (non ultimo, perché è contrassegnato con spoiler); in primo luogo, dove ottieni la cifra di 2 MB per un SDF di 128x128? In secondo luogo, perché consideri 2 MB un utilizzo della memoria particolarmente pesante? Sono d'accordo che non è sostanziale, ma sembra una piccola parte dell'utilizzo complessivo della memoria della mappa. E terzo, in che modo la generazione del campo in tempo reale salverà quella memoria? Hai ancora bisogno di archiviare esattamente gli stessi dati, sia che siano generati al volo o pre-calcolati, no?
Steven Stadnicki,

Più in generale, potrebbe essere che gli SDF non siano la tecnica di cui hai bisogno. Maggiori informazioni sulla tua situazione specifica (conteggio statico degli ostacoli, conteggio dinamico degli ostacoli, ecc.) E precisamente quale effetto speri di ottenere sarebbe utile nel cercare di individuare ciò che è più probabile ti sia utile.
Steven Stadnicki,

1
Se avessi generato il campo distanza in tempo reale, avrei creato quei 2 MB solo una volta per fotogramma (l'overhead della memoria totale sarebbe sempre la memoria necessaria per un campo distanza, poiché ho bisogno solo di quello per lo schermo). Se ho una mappa più grande del mio esempio 1000x128 (penso che le grandi mappe Terraria vadano ben oltre i 10000) ho bisogno di una di quelle 2mb per ogni submap 1000x128 di quella mappa. Il motivo per cui ho bisogno di SDF in primo luogo è descritto nella prima domanda che ho collegato all'inizio di questa domanda (è per il cast shadow shadow della GPU).
TravisG,

1
@heishe stai provando a generare 2Mb di dati una volta per frame? Sul serio?
Kaao

Risposte:


9

Catalin Zima spiega come realizzare ombre 2D dinamiche nel suo articolo - e usa un campo di distanza con segno (da quello che posso dire che è solo un nome di fantasia per un buffer di ombre in questo contesto). Il suo metodo ha bisogno di una GPU e la sua implementazione così com'è non è la migliore (la sua è scesa sotto i 60Hz a circa 20 luci sulla mia macchina, la mia ha ottenuto circa 500 luci); che è prevedibile poiché ha favorito la chiarezza del codice rispetto alla velocità.

Implementazione

Esattamente come implementato da lui:

  1. Rendi tutte le ruote d'ombra in una trama.
  2. Calcola la distanza dal centro della luce per ciascun pixel e assegna quel valore al RGB dei pixel opachi.
  3. Distorci l'immagine in modo che rappresenti come una videocamera 3D avrebbe visto quei pixel.
  4. Schiaccia l'immagine in un'immagine di dimensioni 2xN usando il ridimensionamento insolito descritto nel suo articolo (un ridimensionamento semplice non funzionerà).
  5. L'immagine 2xN è ora il campo della distanza con segno per tutti e quattro i quadranti della luce (ricorda che un quadrante è fondamentalmente un singolo frustum della telecamera a 90 gradi).
  6. Rendering della mappa luminosa.
  7. Sfoca la mappa luminosa (in base alla distanza dalla luce) in modo da ottenere ombre morbide.

La mia implementazione finale è stata (ogni passaggio è un singolo shader):

  1. Fare (1).
  2. Fare (2) e (3) .
  3. Fare (4). La sua implementazione è molto lenta: se puoi provare a usare GPGPU per questo. Non ho potuto usare GPGPU (XNA) quindi quello che ho fatto è stato:
    • Imposta una mesh in cui le prime colonne N / 2 erano rappresentate da quadrupoli N / 2 con la stessa posizione ESATTA (che copre la prima colonna del buffer finale) ma coordinate della trama diverse (stessa cosa per le seconde colonne N / 2)
    • Disattiva i test di profondità sulla GPU.
    • Utilizzare la funzione di fusione pixel MIN.
  4. Fare (6) e (7).

È abbastanza ingegnoso: è fondamentalmente una traduzione diretta di come le ombre vengono gestite in 3D in 2D.

insidie

La trappola principale è che alcuni oggetti non dovrebbero essere ombreggiati: nel mio esempio stavo scrivendo un clone Liero (worm in tempo reale) e quindi non volevo, ad esempio, che i worm dei giocatori fossero ombreggiati (almeno quello sullo schermo di ogni giocatore). Tutto ciò che ho fatto per questi oggetti "speciali" è stato ridisegnarli come ultimo passo. L'ironia era che la maggior parte degli oggetti non erano in ombra (worm, primo piano orizzontale), quindi qui c'è un problema di eccedenza.


La tua regolazione al metodo di ridimensionamento è stata l'unica cosa per accelerarla per gestire 500 luci sopra i 60 fps?
TravisG,

OK, accetterò la risposta poiché risolve il mio problema originale, ma in realtà non risponde a ciò per cui ho dato la grazia. Aspetterò e forse qualcuno arriva a lungo per spiegare uno dei numerosi metodi O (N) per la generazione di campi a distanza firmati in giro.
TravisG,

@heishe riguardo alla tua prima domanda: non sono sicuro. Ho fatto tutte le ottimizzazioni in un unico passaggio - penso di ricordare di averlo spento e di aver osservato il calo del framerate sostanzialmente. Nel complesso 6 chiamate di richiamo per luce uccideranno il tuo framerate. Come ho detto sembra, da quello che posso dire, hai un 4 campi di distanza firmati al punto (5) - ma qualcuno che ne sa di più dovrà confermarlo.
Jonathan Dickinson,

Bene, è un caso molto speciale di un campo di distanza firmato. In un normale campo di distanza con segno, ogni pixel contiene la distanza dall'ostacolo più vicino. In questo algoritmo, il campo distanza contiene solo un ostacolo e anche l'ostacolo è solo 1 pixel nell'intera immagine (la sorgente luminosa), motivo per cui questo campo distanza può essere generato in O (N).
TravisG,

1
@heishe ecco il mio shader: gist.github.com/2384073 . DistortPS è 2 + 3.
Jonathan Dickinson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.