\\ Home : Articoli : Stampa
Luci
Di robydx (del 07/05/2007 @ 20:05:36, in Direct3D10, linkato 1847 volte)

L’elemento più importante nel mondo visivo è la luce. Tutto ciò che noi vediamo dipende unicamente da questo fattore e per questo le luci sono l’elemento chiave di ogni rappresentazione visiva, dal disegno agli effetti speciali nei film.

Per prima cosa bisogna partire da un presupposto importante: la luce reale non può essere rappresentata, e forse non lo sarà mai. La luce è composta da infinite particelle che rimbalzano contro gli oggetti determinandone il colore.

Quello che possiamo fare è solo cercare di simularla, cosa che nei videogiochi è la sfida più difficile. Nei software di rendering dove non serve che il risultato sia in tempo reale si usano tecniche in cui si simula la luce proiettando un certo numero di raggi facendogli eseguire il percorso che farebbe la luce. Nei videogiochi non si può. Ogni vertice non è in grado di vedere tutti gli altri, così come nessun pixel ne è in grado. L’illuminazione è gestista singolarmente a livello di vertice e di pixel. Per questo sono state elaborate dai matematici diverse formule matematiche di illuminazione atte a simulare la luce: queste formule prendono il nome di reflection model (modelli di riflessione). Esistono centinaia di formule base, ognuna che differisce dalle altre per complessità o tipo di materiale. In questo tutorial partiremo dalla formula più semplice: la Lambertian.

Innanzitutto bisogna partire dal concetto di normale, già anticipato nei precedenti tutorial. La normale è la direzione in cui è rivolta una superficie, quindi nel caso di directX parliamo di triangoli. Tale vettore è scomposto in 3 vettori in modo da essere assegnato al vertice. In questo caso si parla di normale al vertice.

Un vettore normale è rappresentato da 3 valori XYZ ed ha lunghezza pari ad 1. La normale è l’elemento fondamentale per tutti i modelli di illuminazione esistenti.

Per chi non fosse pratico di algebra vettoriale (materia che consiglio a tutti di approfondire) una direzione è definita come differenza tra il punto di destinazione e quello di origine.

Ad esempio dati 2 punti A (3,1) e B (7,4) il vettore direzione AB è uguale a (7-3,4-1) ossia (4,3). La lunghezza di AB è 5 (calcolata con pitagora in quanto il vettore AB è l’ipotenusa del triangolo rettangolo avente cateti pari a 4 e a 3). Dividendo 4 e 3 per 5 otteniamo il vettore AB normalizzato ossia

AB=(0.8,0.6)

Questa è una direzione normalizzata. La normale è appunto la direzione tra 2 punti.

L'istruzione normalize in HLSL o D3DXVECTORNormalize in DirectX effettuano le normalizzazioni di vettori.

 

Esistono diversi tipi di sorgenti luminose. Le principali sono quelle direzionali e le luci puntiformi, dette anche omni light. Nel primo caso parliamo di sorgenti luminose che provengono da distanza infinita e che non perdono intensità. Il sole è la tipica fonte luminosa che utilizza le luci direzionali dato che proviene da una distanza sufficientemente ampia. Le luci puntiformi invece provengono da sorgenti vicine alla scena, come lampadine o torce. La loro proprietà è quella di proiettare luce intorno ad essa e non da una sola direzione. In entrambi i casi abbiamo comunque una direzione. Nella luce direzionale la direzione della luce è definito nello stesso modo della normale, mentre nella luce puntiforme avremo come direzione la differenza tra la destinazione (il vertice ad esempio) e la sorgente.

La formula di Lambert dice che l’intensità della luce in un punto è inversamente proporzionale all’angolo che c’è tra il vettore normale ed il vettore incidente della luce.

I = N x -L

Il simbolo x è inteso in questo contesto come prodotto fattoriale. Il prodotto fattoriale tra 2 vettori equivale alla somma degli elementi del vettore ottenuto moltiplicando membro a membro due vettori.

A(2,3,4) x B (4,5,6) = ( 2*4 + 3*5 + 4*6)= 47

Il prodotto tra 2 vettori normalizzati equivale al coseno dell’angolo tra i due vettori e varia da 1 per raggi che guardano nella stessa direzione, 0 per angoli perpendicolari tra loro e -1 per angoli opposti.

Quanto più la luce sarà incidente, tanto più l’intensità tenderà al massimo. Per dare un colore alla luce possiamo infine moltiplicare l’intensità che otteniamo (che è un solo float, ricordate) per il colore del materiale e per il colore della luce (possiamo usare dei float4 per questo). Nel colore del materiale rientra anche quello della texture ovviamente.

Ecco la formula finale

Float4 finalColor = saturate( lightColor * materialColor * dot(N,-L));

La formula può essere ulteriormente raffinata aggiundo formule di attenuazione (ad esempio si può scegliere che la luce diminuisca in base alla distanza o all'angolo), luci ambientali. Ecco un esempio di formula di illuminazione completa basata su lambert

FinalColor = ambientColor + funzioneAttenuazione * lightColor * materialColor * dot(N,L)

 

Vi lascio al demo

Demo