Wednesday, January 14, 2009

Bloom: first steps

I've made some other important steps into the bloom implementation, using pixel shaders. I've discovered how to make a "pipeline" of pixel shaders, a necessary tool to make complex effect like Bloom or HDR.
We have seen, last time, that to make a bloom effect it's necessary to execute more than one shader in sequence.
The effect is obtained:

  1. scaling down the original picture by 16 times
  2. isolating only bright parts of the reducted image
  3. blurring orrizontally and vertically the bright parts
  4. scaling up the result
  5. merge the scaled result with the original frame.

The problem was: how can i use the shader output like input for the next shader step?
I've found a trick:

  • I draw the entire scene and i render it into a texture. I'll call it TexA.
  • I draw a rectangle that fills all the screen, and i apply the texture TexA on it. Then i render it using the first shader. I'll call it ShaderA. The result is not drawed into back buffer (the screen), but into another texture. I'll call it TexB.
  • I draw another rectangle, idendical to the previous, and i apply the teture TexB on it. Then i render it using the secon shader. I'll called it ShaderB. Now if there's no more shaders on the pipeline i'll put it into the back buffer, otherwise i'll iterate the procedure using other textures (TexC, TexD and so on...) one for each shader. To optimize the memory usage i can use only two texture and swap it every cycle.

It works! In the next video you can see a result of applying two pixel shaders to the same scene: a blur effect, then a simple light effect. It's not the final result that i want to obtain but it's essential to make all the trip.

---

Ho fatto alcuni importanti passi nella realizzazione dell'effetto bloom usando i pixel shader. Risultato che non serve esclusivamente ad ottenere l'effetto bloom, ma anche per la realizzazione di molti effetti complessi come ad esempio l'HDR, il motion blur e tanto altro.
Abbiamo visto la volta precedente che per ottenere l'effetto "bloom" e' necessario eseguire piu' di uno shader in sequenza.
Nello specifico l'effetto e' ottenuto:

  1. rimpicciolendo di 16 volte l'immagine originale
  2. isolando le parti luminose dall'immagine rimpicciolita
  3. sfuocando orrizontalmente e verticalmente le parti luminose
  4. ingrandendo di 16 volte l'immagine sfuocata
  5. fondendo assieme l'immagine appena ingrandita all'immagine originale.

Il problema che mi bloccava fino ad oggi era: come puo' l'output di uno shader essere usato come input per lo shader seguente?
E mi e' venuta un'idea:

  • Disegno la scena intera all'interno di una singola Texture anziche' sullo schermo. Chiamo per semplicita' questa texture TexA.
  • Disegno un rettangolo che riempia l'intero schermo e vi applico la texture TexA. Dopodiche' lo elaboro con il primo shader, che chiameremo ShaderA. Il risultato non lo mostro a video ma lo metto in una seconda Texture, che chiamero' TexB.
  • Disegno un altro rettangolo, identico al precedente, solo che a questo applico la texture TexB. Dopodiche' lo elaboro con il secondo shader, che chiameremo ShaderB. A questo punto se vi sono altri shaders da elaborare continuo con la logica precedente utilizzando altre textures (TexC, ecc..), altrimenti mostro il risultato a video, risultato che sara' quindi la somma degli effetti degli shaders applicati uno alla volta all'immagine iniziale

Funziona! Nel video che segue potete vedere ad esempio il risultato di applicare due shader alla medesima scena: un effetto di sfuocamento, e poi un effetto "abbagliamento". Non e' proprio il bloom, ma e' un banco di prova necessario per ottenere il risultato al quale voglio arrivare.

2 comments:

Marte said...

figo!
così potresti ottenere anche nebulose di materiale dentro cui le navi possono "passare" :D

sempre più convinto che potresti realizzare un mix tra simulatore spaziale e fps.. come the precursors

http://www.precursors-game.com/eng/

PdG said...

Mhh... la tecnica che ho descritto e' puramente bidimensionale. Serve ad ottenere effetti che si applicano al rendering completato, in maniera totalmente piatta e che non distingue minimamente le varie parti delle mesh.
Ne e' un esempio il filmato del post precedente: l'effetto "acqua" si applica da meta' schermo in giu', e funziona in maniera del tutto indipendente da quel che si vede sullo schermo.. che tu punti verso il cielo o verso un monte sempre meta' schermo sara' soggetta a movimento sinusoidale.
La nebulosa invece e' un oggetto tridimensionale, che ha una posizione ben precisa nello spazio, delle dimensioni, una forma.
Comunque vedremo :)