Esistono generalmente due metodi per gestirlo. Oggi vengono chiamati rendering in avanti e rendering differito. C'è una variazione su questi due che discuterò di seguito.
Rendering in avanti
Rendi ogni oggetto una volta per ogni luce che lo colpisce. Questo include la luce ambientale. Si utilizza una modalità di fusione additiva ( glBlendFunc(GL_ONE, GL_ONE)
), quindi i contributi di ciascuna luce si sommano. Poiché il contributo di luci diverse è additivo, il framebuffer alla fine ottiene il valore
È possibile ottenere HDR eseguendo il rendering in un framebuffer a virgola mobile. Quindi fai un passaggio finale sulla scena per ridimensionare i valori di illuminazione dell'HDR su un intervallo visibile; questo sarebbe anche il luogo in cui implementate la fioritura e altri post-effetti.
Un miglioramento delle prestazioni comune per questa tecnica (se la scena ha molti oggetti) è l'uso di un "pre-passaggio", in cui si esegue il rendering di tutti gli oggetti senza disegnare nulla sul framebuffer dei colori (utilizzare glColorMask
per disattivare le scritture dei colori). Questo riempie solo il buffer di profondità. In questo modo, se si esegue il rendering di un oggetto dietro un altro, la GPU può saltare rapidamente quei frammenti. Deve ancora eseguire lo shader di vertice, ma può saltare i calcoli dello shader di frammenti in genere più costosi.
Questo è più semplice da codificare e più facile da visualizzare. E su alcuni hardware (principalmente GPU mobili e integrate), può essere più efficiente dell'alternativa. Ma su hardware di fascia alta, l'alternativa generalmente vince per le scene con molte luci.
Rendering differito
Il rendering differito è un po 'più complicato.
L'equazione di illuminazione utilizzata per calcolare la luce per un punto su una superficie utilizza i seguenti parametri di superficie:
- Posizione di superficie
- Normali di superficie
- Colore diffuso in superficie
- Colore speculare superficiale
- Lucentezza speculare superficiale
- Forse altri parametri di superficie (a seconda della complessità dell'equazione dell'illuminazione)
Nel rendering in avanti, questi parametri raggiungono la funzione di illuminazione dello shader di frammento sia passando direttamente dallo shader di vertice, sia estraendo da trame (di solito attraverso coordinate di trama passate dallo shader di vertice), o generati da tutto il tessuto nello shader di frammento basato su altri parametri. Il colore diffuso può essere calcolato combinando un colore per vertice con una trama, combinando più trame, qualunque cosa.
Nel rendering differito, rendiamo tutto questo esplicito. Nel primo passaggio, eseguiamo il rendering di tutti gli oggetti. Ma non rendiamo i colori . Al contrario, eseguiamo il rendering dei parametri di superficie . Quindi ogni pixel sullo schermo ha una serie di parametri di superficie. Questo viene fatto tramite rendering su trame fuori schermo. Una trama memorizzerebbe il colore diffuso come RGB, e possibilmente la lucentezza speculare come l'alfa. Un'altra trama memorizzerebbe il colore speculare. Un terzo memorizzerebbe il normale. E così via.
La posizione di solito non è memorizzata. Viene invece ricostituito nel secondo passaggio dalla matematica che è troppo complesso per entrare qui. Basti dire che usiamo il buffer di profondità e la posizione del frammento dello spazio dello schermo come input per capire la posizione dello spazio della telecamera del punto su una superficie.
Quindi, ora che queste trame contengono essenzialmente tutte le informazioni sulla superficie per ogni pixel visibile nella scena, iniziamo a renderizzare quad a schermo intero. Ogni luce ottiene un rendering quad a schermo intero. Campioniamo dalle trame dei parametri di superficie (e ricostituiamo la posizione), quindi le usiamo solo per calcolare il contributo di quella luce. Questo viene aggiunto (di nuovo glBlendFunc(GL_ONE, GL_ONE)
) all'immagine. Continuiamo a farlo finché non finiamo le luci.
L'HDR di nuovo è un passaggio post-process.
Il principale svantaggio del rendering differito è l'antialiasing. Richiede un po 'più di lavoro per antialias correttamente.
Il vantaggio più grande, se la tua GPU ha molta larghezza di banda di memoria, sono le prestazioni. Eseguiamo il rendering della geometria effettiva una sola volta (o 1 + 1 per luce con ombre, se stiamo eseguendo la mappatura delle ombre). Non passiamo mai del tempo su pixel o geometria nascosti che non sono più visibili dopo questo. Tutto il tempo di passaggio dell'illuminazione viene speso per cose che sono effettivamente visibili.
Se la tua GPU non ha molta larghezza di banda di memoria, il passaggio della luce può davvero farti male. Trarre da 3-5 trame per pixel dello schermo non è divertente.
Pre-Pass leggero
Questa è una sorta di variazione sul rendering differito che presenta interessanti compromessi.
Proprio come nel rendering differito, i parametri della superficie vengono sottoposti a rendering in un set di buffer. Tuttavia, hai abbreviato i dati di superficie; gli unici dati di superficie che ti interessano in questo momento sono il valore del buffer di profondità (per ricostruire la posizione), la lucentezza normale e speculare.
Quindi per ogni luce, calcoli solo i risultati dell'illuminazione. Nessuna moltiplicazione con i colori delle superfici, niente. Solo il punto (N, L) e il termine speculare, completamente senza i colori della superficie. I termini speculari e diffusi dovrebbero essere conservati in buffer separati. I termini speculari e diffusi per ciascuna luce sono riassunti nei due buffer.
Quindi, si esegue nuovamente il rendering della geometria, utilizzando i calcoli di illuminazione speculare e diffusa totali per eseguire la combinazione finale con il colore della superficie, producendo così la riflessione complessiva.
I lati positivi qui sono che si ottiene indietro il multicampionamento (almeno, più facile che con differito). Si esegue meno rendering per oggetto rispetto al rendering in avanti. Ma la cosa principale oltre che differita che questo fornisce è un momento più facile per avere diverse equazioni di illuminazione per diverse superfici.
Con il rendering differito, in genere si disegna l'intera scena con lo stesso shader per luce. Quindi ogni oggetto deve usare gli stessi parametri del materiale. Con il pre-passaggio della luce, puoi dare a ogni oggetto uno shader diverso, in modo che possa eseguire da solo il passaggio finale di illuminazione.
Ciò non offre la stessa libertà del caso di rendering in avanti. Ma è ancora più veloce se hai la larghezza di banda delle trame da risparmiare.