Rendering in avanti e rendering differito

Se sei uno sviluppatore di giochi 3D, probabilmente hai già trovato i termini rendering avanzato e rendering differito nella tua ricerca di motori grafici moderni. E, spesso, dovrai sceglierne uno da usare nel tuo gioco. Ma cosa sono, come si differenziano e quale scegliere?

Rendering differito per molte luci (Immagine per gentile concessione di Hannes Nevalainen)

Moderne pipeline grafiche

Per iniziare, dobbiamo capire un po 'di pipeline grafiche moderne o programmabili.

Nel corso della giornata, eravamo limitati a ciò che la pipeline grafica della scheda video aveva. Non è stato possibile modificare il modo in cui ha disegnato ciascun pixel, a parte l'invio di una trama diversa e non siamo riusciti a deformare i vertici una volta che erano sulla carta. Ma i tempi sono cambiati, e ora abbiamo pipeline grafiche programmabili. Ora possiamo inviare il codice alla scheda video per cambiare il modo in cui i pixel appaiono, dando loro un aspetto irregolare con mappe normali e aggiungendo riflessione (e una grande dose di realismo).

Questo codice è sotto forma di geometria, vertice, e frammenti di shader, e in sostanza cambiano il modo in cui la scheda video rende i tuoi oggetti.


Vista semplificata di una pipeline grafica programmabile

Rendering avanzato

Il rendering avanzato è la tecnica di rendering standard, pronta all'uso, utilizzata dalla maggior parte dei motori. Fornisci alla scheda grafica la geometria, la proietta e la suddivide in vertici, quindi quelli vengono trasformati e suddivisi in frammenti, o pixel, che ottengono il trattamento di rendering finale prima che vengano passati sullo schermo.


Inoltro di rendering: shader di geometria a shader di vertici per frammentare Shader

È piuttosto lineare e ogni geometria viene passata lungo il tubo uno alla volta per produrre l'immagine finale.


Rendering differito

Nel rendering posticipato, come suggerisce il nome, il rendering viene differito un po 'fino a quando tutte le geometrie non hanno passato la pipa; l'immagine finale viene quindi prodotta applicando l'ombreggiatura alla fine.

Ora, perché dovremmo farlo?


Rendering differito: Geometria al vertice per frammentare gli shader. Passato a più obiettivi di rendering, quindi ombreggiato con illuminazione. Illuminazione differita è una modifica del rendering differito che riduce la dimensione del G-buffer usando più passaggi sulla scena.

Prestazioni di illuminazione

L'illuminazione è la ragione principale per percorrere una rotta rispetto all'altra. In una pipeline standard di rendering in avanti, i calcoli di illuminazione devono essere eseguiti su ogni vertice e su ogni frammento nella scena visibile, per ogni luce nella scena.

Se hai una scena con 100 geometrie e ogni geometria ha 1.000 vertici, potresti avere circa 100.000 poligoni (una stima molto approssimativa). Le schede video possono gestirlo abbastanza facilmente. Ma quando quei poligoni vengono inviati allo shader di frammenti, è qui che avvengono i costosi calcoli di illuminazione e può verificarsi il vero rallentamento.

Gli sviluppatori cercano di spingere quanti più calcoli di illuminazione nel vertex shader possibile per ridurre la quantità di lavoro che il framment shader deve fare.

I costosi calcoli di illuminazione devono essere eseguiti per ogni frammento visibile di ogni poligono sullo schermo, indipendentemente se si sovrappone o è nascosto da altri frammenti di un poligono. Se il tuo schermo ha una risoluzione di 1024x768 (che è, con ogni mezzo, non ad altissima risoluzione) hai circa 800.000 pixel che devono essere renderizzati. Potresti facilmente raggiungere un milione di operazioni sui frammenti ogni fotogramma. Inoltre, molti dei frammenti non arriveranno mai sullo schermo perché sono stati rimossi con test di profondità, e quindi il calcolo dell'illuminazione è stato sprecato su di essi.

Se hai un milione di quei frammenti e all'improvviso devi rendere nuovamente quella scena per ogni luce, sei saltato su [num lights] x 1.000.000 operazioni di frammentazione per fotogramma! Immagina se tu avessi una città piena di lampioni dove ognuno è una fonte di luce puntiforme ...

La formula per stimare questa complessità di rendering in avanti può essere scritta, in notazione O grande, come O (num_geometry_fragments * num_lights). Qui puoi vedere che la complessità è direttamente correlata al numero di geometrie e al numero di luci.

frammenti sono potenziali pixel che finiranno sullo schermo se non vengono abbattuti dal test di profondità.

Ora, alcuni motori ottimizzano questo, tagliando le luci che sono lontane, combinando luci o usando mappe luminose (molto popolari, ma statiche). Ma se vuoi luci dinamiche e molte di esse, abbiamo bisogno di una soluzione migliore.


Rendering differito al salvataggio

Il Rendering differito è un approccio molto interessante che riduce il conteggio degli oggetti, e in particolare il conteggio dei frammenti totali, ed esegue i calcoli di illuminazione sui pixel sullo schermo, usando quindi la dimensione della risoluzione invece del conteggio totale dei frammenti.

La complessità del rendering differito, nella notazione O grande, è: O (screen_resolution * num_lights).

Puoi vedere che ora non importa quanti oggetti hai sullo schermo che determina il numero di luci che usi, così puoi aumentare felicemente il numero di luci. (Questo non significa che puoi avere oggetti illimitati, devono ancora essere disegnati nei buffer per produrre il risultato finale del rendering.)

Vediamo come funziona.


The Guts of Deferred Rendering

Ogni geometria viene renderizzata, ma senza ombreggiatura, a diversi buffer di spazio dello schermo che utilizzano più obiettivi di rendering. In particolare, la profondità, le normali e il colore sono tutti scritti per separare i buffer (immagini). Questi buffer vengono quindi combinati per fornire informazioni sufficienti per ciascuna luce per illuminare i pixel.


Colore, profondità e buffer normali. (Immagini di astrofa, tramite Wikimedia Commons.)
Risultato dell'illuminazione finale (ombreggiatura) generato utilizzando i tre buffer. (Immagine di astrofa, tramite Wikimedia Commons.)

Sapendo quanto è distante un pixel e il suo vettore normale, possiamo combinare il colore di quel pixel con la luce per produrre il nostro rendering finale.


Quale scegliere?

La risposta breve è, se stai usando molte luci dinamiche, allora dovresti usare il rendering differito. Tuttavia, ci sono alcuni svantaggi significativi:

  • Questo processo richiede una scheda video con più destinazioni di rendering. Le vecchie schede video non hanno questo, quindi non funzionerà su di esse. Non c'è soluzione per questo.
  • Richiede una larghezza di banda elevata. Stai inviando grandi buffer in giro e vecchie schede video, di nuovo, potrebbe non essere in grado di gestirlo. Non c'è soluzione per questo, neanche.
  • Non puoi usare oggetti trasparenti. (A meno che non si combini il rendering differito con il rendering in avanti solo per quegli oggetti trasparenti, allora si può aggirare questo problema).
  • Non c'è anti-aliasing. Bene, alcuni motori vorrebbero crederci, ma ci sono soluzioni a questo problema: il rilevamento dei bordi, FXAA.
  • È consentito un solo tipo di materiale, a meno che non si utilizzi una modifica del rendering differito denominata Deferred Lighting.
  • Le ombre dipendono ancora dal numero di luci e il rendering posticipato non risolve nulla qui.

Se non hai molte luci o vuoi essere in grado di girare su hardware vecchio, allora dovresti continuare con il rendering in avanti e sostituire le tue numerose luci con mappe di luce statiche. I risultati possono ancora sembrare sorprendenti.


Conclusione

Spero che abbia fatto luce sull'argomento. Le tue opzioni sono lì per risolvere i tuoi problemi di rendering, ma è molto importante scegliere quello giusto all'inizio dello sviluppo del gioco per evitare modifiche difficili in seguito.