• Non ci sono risultati.

La visualizzazione di immagini che derivano da frattali pu`o trarre grande vantaggio dagli shader. Queste immagini non possono essere precalcolate, perch´e possono essere assimilate a immagini a risoluzione infinita. D’altra parte, i pixel sono gli unici punti in cui importa veramente conoscere il valore dell’immagine, per cui ci si pu`o limitare a calcolarla in questi punti.

Anche la CPU potrebbe eseguire l’algoritmo per ogni pixel, ma i Fragment Sha-der sono fatti apposta per processare ogni frammento di una primitiva, e possono anche sfruttare la capacit`a di parallelismo della GPU. Basta visualizzare una primi-tiva attraverso un particolare Fragment Shader che esegue l’algoritmo del frattale per ogni frammento e imposta il colore di conseguenza.

In questo esempio, `e stata utilizzata la classica formula per l’insieme di Man-delbrot:

zn+1 = zn2 + c

I vari zke c sono numeri complessi. Per definizione, posto z0 = 0, c appartiene all’insieme di Mandelbrot se znconverge per n tendente all’infinito.

L’unico modo per sapere in un tempo finito se la successione converge oppure no `e calcolarne un gran numero di valori e dire che `e divergente se il modulo di uno di essi supera un valore molto grande rispetto ai dati iniziali. Questo algoritmo approssimato ha due svantaggi: `e necessario decidere arbitrariamente il valore mas-simo, e soprattutto bisogna definire un numero massimo di iterazioni, altrimenti nei casi convergenti l’algoritmo proseguirebbe all’infinito.

Per ottenere l’insieme di Mandelbrot, si dovrebbe partire da z0 = 0 e inserire le coordinate (di modello) del frammento in c, con la formula c = x + iy (la z `e co-stante). Dopodich´e si assegnerebbe un colore diverso al pixel a seconda del numero di iterazioni necessarie per far superare alla successione il valore prestabilito.

In questo esempio, invece, si `e tentato di ottenere qualcosa di pi`u originale. Si assegnano le coordinate del frammento a z0 e c invece `e costante. Il colore `e assegnato in funzione del contenuto di z quando il frattale diverge: i due colori sono mischiati insieme in funzione del valore di parte reale e immaginaria. Se la successione converge, invece, viene scelto un colore che `e la media tra i due.

Capitolo 4. Algoritmi grafici

Il concetto matematico non `e molto diverso, ma questo spiega perch´e compare quell’insolita forma spinosa invece di una classica immagine di un frattale famoso. Il listato 4.24 riporta il fragment shader utilizzato.

u n i f o r m v e c 2 initC; u n i f o r m i n t numIter; v a r y i n g v e c 3 position; u n i f o r m v e c 4 color1; u n i f o r m v e c 4 color2; v e c 2 Fractal(f l o a t x,f l o a t y) { v e c 2 c = initC ; v e c 2 z = v e c 2( x , y ) ; f o r (i n t i = 0 ; i < numIter ; i++) { v e c 2 sum = z + c ;

z = v e c 2( sum . x*sum . x − sum . y*sum . y , 2 . 0 * sum . x*sum . y ) ;

i f (a b s(d o t( z , z ) ) > 3 0 . 0 ) r e t u r n n o r m a l i z e( z ) ; } r e t u r n v e c 2( 0 . 5 , 0 . 5 ) ; } v o i d main( ) {

v e c 2 res = Fractal ( position . x , position . y ) ;

v e c 4 color = res . x * color1 + res . y * color2 ;

g l F r a g C o l o r = c l a m p( color , 0 . 0 , 1 . 0 ) ; }

Listato 4.24: Fragment shader per la visualizzazione di un frattale.

Il vertex shader 4.25 `e molto semplice e si limita a passare avanti la posizione originale di ciascun vertice, che sar`a poi interpolata nel fragment shader:

Le immagini in figura 4.10 mostrano il risultato della visualizzazione di un qua-drato con questi shader. La variabile initC `e stata impostata a 0.285 + 0.013i, mentre il numero di iterazioni numIter `e 400. I colori 1 e 2 sono rispettivamente il verde (rgb: 0.0,0.9,0.0) e il blu (rgb: (0.0,0.0,0.9)).

Per finire, si nota che un’immagine di un frattale generato in questo modo si comporta esattamente come una texture (immagine a dimensione finita). La pri-mitiva attraversa tutta la pipeline e pu`o essere traslata, ruotata e illuminata. Pu`o

Capitolo 4. Algoritmi grafici

v a r y i n g v e c 3 position;

v o i d main( ) {

v e c 4 newpos = g l V e r t e x;

position = newpos . xyz / newpos . w ;

g l P o s i t i o n = g l M o d e l V i e w P r o j e c t i o n M a t r i x * newpos ;

}

Listato 4.25: Vertex shader.

Figura 4.10: Tra ciascuna immagine e quella alla sua sinistra c’`e un fattore di ingrandimento di circa 2,5.

comparire nella scena insieme ad altre primitive pi`u tradizionali e i suoi frammenti subiscono il depth test come le altre (figura 4.11, a sinistra).

Figura 4.11: A sinistra, la primitiva su cui `e il frattale, ruotata ed illuminata. A destra, il frattale con ingrandimento pari a 13780 volte, che comincia ad essere eccessivo per la precisione delle variabili floating point utilizzate.

Capitolo 5

Conclusione

Naturalmente, il discorso sugli shader `e tutt’altro che concluso. Essi sono molto di pi`u di un effetto grafico, un insieme di effetti grafici o un algoritmo. La programma-zione degli shader consente l’uso di un nuovo linguaggio e di un intero processore, perci`o `e impossibile circoscrivere il numero di algoritmi e funzionalit`a che sono state sviluppate in pi`u di otto anni e possono essere sviluppate in futuro.

Gi`a in questa trattazione, per motivi di tempo e di spazio, `e stato necessario ridurre il numero di esempi riportati, e di questi semplificare il codice. Del resto, ogni nuovo shader offriva spunti per modifiche e varianti, le quali a loro volta po-tevano generare nuove idee, sempre pi`u complicate. Proseguendo per questa strada, il percorso non avrebbe mai avuto una fine.

E ci sarebbero state anche altre strade da prendere. La GPU sta cominciando a diventare un processore di uso generale, come la CPU. Gi`a oggi alcuni programmi di uso comune possono avvalersi della sua capacit`a di elaborazione parallela per eseguire calcoli che hanno poco o nulla a che fare con la visualizzazione di grafica tridimensionale sullo schermo. Un esempio di questo sono i decoder di formati compressi per i filmati, che possono sfruttare l’accelerazione hardware della GPU.

Invece, si spera di aver mostrato almeno la direzione intrapresa dalla program-mazione grafica. Non `e pi`u questione di impostare i parametri della Fixed Func-tion Pipeline per ottenere l’effetto desiderato tra quelli disponibili. Piuttosto, si modifica la pipeline per ottenere quell’effetto in modo pi`u efficiente, versatile e personalizzabile.

Capitolo 5. Conclusione

Vista la mole di lavoro necessaria per reimplementare da zero, ad esempio, l’il-luminazione, questo approccio ha certamente i suoi svantaggi. Eppure, gli esempi riportati dimostrano che `e stato possibile generare effetti altrimenti molto difficili e inefficienti da ottenere usando solo la programmazione tradizionale della CPU.

Bibliografia

[1] Mike Bailey and Steve Cunningham, Graphics Shaders: Theory and Practice. AK Peters, 2009

[2] OpenGL 2.1 Reference Pages

http://www.opengl.org/sdk/docs/man/ [3] OpenGL 4.2 Reference Pages

http://www.opengl.org/sdk/docs/man4/

[4] OpenGL Shading Language (GLSL) Reference Pages http://www.opengl.org/sdk/docs/manglsl/

[5] GLSL Tutorial Lighthouse3d.com

http://www.lighthouse3d.com/tutorials/glsl-tutorial/ [6] GLSL Core Tutorial Lighthouse3d.com

http://www.lighthouse3d.com/tutorials/glsl-core-tutorial/ [7] The Little Grasshopper

Documenti correlati