Casino online









Mercato forex






B21. Riflessi


Quando guardiamo la superficie dell'acqua, essa non appare sempre uniforme, ma, dato che riflette anche la luce del sole, ci saranno certi punti molto più luminosi di altri. Questo è quello che succede, infatti, quando si guarda l'immagine riflessa del sole, ad esempio, in una pozzanghera. Nella cube map che abbiamo usato come skybox c'è il "disegno" di un sole, ed esso viene correttamente riflesso sulla sull'acqua senza problemi. La situazione non impone di aggiungere ulteriori riflessi, ma lo si può fare. Questa aggiunta rappresenta una raffinatezza usabile anche in altri casi.
Per trovare quei pixel che vengono colpiti dalla luce solare, basta semplicemente riflettere la direzione della luce sulla superficie. Questa operazione viene effettuata usando la funzione reflect di hlsl, che accetta come primo parametro il vettore da riflettere, e come secondo parametro la normale del punto di riflessione. Aggiungiamo quindi questo codice alla fine del pixel shader, dopo il calcolo di WaterColor:
float3 ReflectionVector = -reflect(LightDirection, Normal);
In questo modo, si ottiene il vettore riflesso di LightDirection rispetto alla normale del punto colpito dalla luce. Potete osservare il risultato che si dovrebbe ottenere in questo schema:


Luce riflessa

Tuttavia, la funzione reflect non restituisce il vettore verde esattamente com'è nella figura, ma invertito, con la punta rivolta verso O (ossia esattamente simmetrica al vettore Luce rispetto alla normale). Per questo motivo dobbiamo mettere un meno davanti al risultato della funzione per ottenere il vettore giusto.
Ora, sappiamo che la luce intensa riflessa dai punti colpiti dal sole ci giunge solo se stiamo guardando verso quei punti. Perciò, per sapere quanta luce l'osservatore riceva, dobbiamo valutare di quanto il vettore riflessione sia uguale al vettore sguardo. Se vi ricordate, per calcolare "quanto coincidenti" (passatemi il termine) siano due vettori, si utilizza il prodotto scalare dot, che restituisce il coseno dell'angolo compreso tra i due. Immettiamo questo risultato in un fattore che chiamiamo Specular:
//Vector e Normal sono definiti nella parte precedente
//del pixel shader
float3 ReflectionVector = -reflect(LightDirection, Normal);
//Ricordate che dot richiede, in questo caso, due vettori di
//lunghezza unitaria
float  Specular = dot(normalize(ReflectionVector), normalize(Vector));
Possiamo ora sommare questo valore al colore di output del pixel shader, per esaltarne la luminosità:
Output.Color.rgb += Specular;
E otterremo questo magnifico risultato:


Un effetto alquanto strano

Non era proprio quello che ci si era aspettati, a dire il vero. Questo succede perchè la luce viene riflessa ed espansa su tutta la superficie disponibile e quando il coseno risulta negativo (ossia quando si voltano le spalle alla luce) il colore non viene esaltato ma scurito. Possiamo eliminare questo vistoso difetto elevando il coefficiente Specular ad una potenza molto grande (e pari): in questo modo non solo si elimina l'area scura che deturpa la bellezza del paesaggio, ma si restringe anche la luce ad un'area molto piccola. Infatti, usando una funzione esponenziale, i valori bassi del coseno tenderanno ad annullarsi e quelli alti a divenire ancora più evidenti. Potete capire meglio questo concetto osservando il grafico seguente


Approssimazione della funzione

Questo grafico rappresenta la funzione |2cos(x/4)|5. Il coefficiente 2 serve per rendere evidente i picchi (se avessi lasciato solo coseno, il grafico sarebbe oscillato tra 0 e 1: in questo modo oscilla tra 0 e 25); il fattore 1/4 nell'argomento del coseno serve per allargare la funzione in modo da renderla meglio osservabile (altrimenti sarebbe stata molto stretta); il valore assoluto, invece, serve per annullare l'oscillazione negativa del coseno, in quanto la potenza 5 è dispari e fa permanere il segno. Insomma, tralasciando tutti gli accorgimenti che ho immesso per rendere la funzione "più bella" esteticamente, potete osservare che, solo con la quinta potenza, essa oscilla vertiginosamente, e raggiunge picchi molto alti in un intervallo esiguo. Pensate al suo andamento se elevassimo, ad esempio, alla 256-esima potenza! Se non riuscite a pensarlo, allora provate (usando la funzione pow, che è identica a Math.Pow):
Specular = pow(Specular, 256);        
Output.Color.rgb += Specular;
Otterrete questo:


Il vero riflesso

Un risultato molto migliore di prima, vero? Tuttavia, possiamo renderlo ancora migliore. Infatti, in questo codice presumiamo che la normale sia la stessa (0,1,0) in tutti i punti. Ma nei tutorial precedenti abbiamo introdotto apposta una bump map per rendere la superficie increspata. Introduciamo questa modifica anche sulla luce:
Normal = (BumpColor.rbg - 0.5f) * 2.0f;
float3 ReflectionVector = -reflect(LightDirection, Normal);
float  Specular = dot(normalize(ReflectionVector), normalize(Vector));
Specular = pow(Specular, 256);        
Output.Color.rgb += Specular;



Risultato finale








 

The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.