OpenGL Lighting

La possibilità di modificare colore intensità e posizione della luce all'interno della scena permette di ricreare ogni tipo di illuminazione con un elevata fedeltà.

Il sistema di illuminazione di OpenGL mette a disposizione un numero limitato di fonti di luce che per la maggior parte delle implementazioni è uguale a 8, esse vengono identificate dall'etichetta GL_LIGHTi dove i è il numero della luce partendo da 0. Anche se apparentemente questo numero sembra piuttosto piccolo in realtà in pochissime circostanze avremo più di 8 luci all'interno della stessa scena, ciò è comprensibile anche solo per l'oneroso compito di eseguire le operazioni di calcolo dell'illuminazione per più di 8 volte.
Se nella scena sono comunque presenti più luci di quante rese disponibili dovremo decidere quali attivare e quali disattivare in base alla posizione dell'osservatore.

Le componenti della luce
OpenGL suddivide una fonte di luce in 3 componenti: Ambient, Diffuse e Specular.

Ambient Light
Viene considerata Ambient Light la luce di cui non si riesce ad identificare la direzione e di conseguenza nemmeno una sorgente ben precisa, si tratta dei raggi che sono stati riflessi talmente tante volte che ormai se ne è persa traccia ma che comuque illuminano la scena. La luce all'interno di una stanza ha una grande componente ambientale perchè le mura e gli altri oggetti presenti l'hanno riflessa più volte, al contrario una luce posizionata all'esterno, ad esempio in mezzo a un prato, ha una bassisima componente ambientale perchè non viene riflessa da nessun oggetto e si disperde nel buio.

Diffuse Light
La componente diffuse è la più importante e corrisponde alla descrizione che normalmente utilizziamo per la luce, ha una direzione ben precisa ma quando colpisce una superficie viene riflessa in modo equo in tutte le direzioni. Al contrario della componente ambientale che illumina allo stesso modo tutta la scena la componente diffuse illumina maggiormente i poligoni perpendicolari alla direzione dei suoi raggi e meno i poligoni che sono rivolti nelle altre direzioni. Tutte le fonti di luce che hanno una posizione o una direzione precisa hanno un alta componente diffuse.

Specular Light
La componente specular ha una direzione ben precisa e quando viene riflessa non si disperde in tutte le direzioni ma ne assume un'altra in base all'angolo di incidenza. La componente speculare viene spesso associata alla luminosità, ciò che permette ad un oggetto di sembrare lucido.

E' importante notare che il colore di un determinato oggetto sottoposto ad illuminazione non dipende solamente dalle componenti della luce, infatti tutte i poligoni sono contrassegnati con un determinato materiale il cui valore è dato da parametri simili a quelle delle luci che ne descrivono il comportamento. Ad esempio un materiale con un alto valore speculare, come il metallo, riflette molta più componente speculare e quindi apparirà più brillante di un materiale con una bassa componente speculare, come ad esempio la stoffa.

Abilitare l'illuminazione
Per poter usufruire dell'illuminazione dobbiamo prima attivarla:
glEnable(GL_LIGHTING);

Inoltre dobbiamo attivare le luci che abbiamo intenzione di utilizzare:
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT4);
    
Manipolare i parametri di illuminazione
Per modificare i parametri delle luci utilizziamo la famiglia di funzioni glLight*()
void glLight{if](GLenum light, GLenum pname, TYPE param);
void glLight{if]v(GLenum light, GLenum pname, TYPE *param);

light è l'identificativo della luce
pname è il parametro della da modificare;
param è il valore del parametro

Posizionare la luce
Per posizionare la luce utilizziamo il parametro GL_POSITION:

float position[4] = {0.0, 10.0, 0.0, 1.0};
glLightfv(GL_LIGHT0, GL_POSITION, position);

In questo modo abbiamo posizionato la luce in alto di 10 unità.
L'ultimo valore dell'array è sostanzialmente un flag che serve a indicare ad OpenGL se si tratta di una positional light oppure di una directional light.
Una positional light è una luce che viene posta nelle vicinanze della scena e illumina in tutte le direzioni a meno che non gli venga applicato un cono di illuminazione (spotlight) e si ottiene impostando l'ultimo valore dell'array a 1.0.
Una directional light è una luce che sta a una distanza virtualmente infinita dalla scena, l'esempio più lampante di directional light è il sole. La distanza infinita implica che i raggi della fonte di luce siano paralleli quando colpiscono gli oggetti della scena ottenendo così un effetto diverso da quello di una luce posizionata vicino i cui raggi non sono paralleli tra loro. Si ottiene settando il quarto valore dell'array a 0.0.
Quando utilizziamo una directional light gli altri valori dell'array non indicano più la posizione della fonte di luce bensì la direzione dalla quale la luce proviene.
Il codice seguente crea una directional light che proviene dalla asse z positivo e quindi illuminerà in direzione dell'asse z negativo:  

float position[4] = {0.0, 0.0, 1.0, 0.0};
glLightfv(GL_LIGHT0, GL_POSITION, position);

Creare una SpotLight
Come accennato prima possiamo trasformare una positional light in una spotlight applicando ad essa un cono di luce, una direzione e un valore di focus.
Per determinare l'ampiezza del cono modifichiamo il parametro GL_SPOT_CUTOFF che ci permette di impostare l'angolo tra l'asse della luce e il raggio più esterno, questo valore viene moltiplicato per 2 al fine di calcolare l'intero diametro della luce. Solitamente la spotlight è disabilitata e i raggi irradiano a 360, questo vuol dire che il parametro di CUTOFF è di 180. Il massimo valore che è consentito impostare (a parte quello speciale di 180 che serve a disabilitare la spotlight) è di 90 dal quale risulterebbe un cono di 180.
Per indicare la direzione modifichiamo il parametro GL_SPOT_DIRECTION e passiamo un vettore x,y,z che contenga la direzione della luce.
Infine è possibile modificare il focus della spotlight tramite GL_SPOT_EXPONENT che influisce sulla concentrazione della luce al centro della spotlight, quando assume un valore molto alto avremo una luce molto intensa al centro del cono e via via più attenuata all'esterno, se il suo valore è uguale a 0.0 la luce sarà ugualmente intensa in tutte le parti del cono.

Colore e Intensità
Per impostare il colore e l'intensità delle varie componenti della luce utilizziamo la funzione glLight*() con i seguenti
parametri ognuno dei quali modifica una delle componenti analizzate precedentemente:

- GL_AMBIENT
- GL_DIFFUSE
- GL_SPECULAR

I vettori da specificare devono contenere le componenti RGBA.
Ad esempio il seguente codice:

float light_diffuse[4] = {0.0, 1.0, 0.0, 1.0};
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
float light_specular[4] = {1.0, 1.0, 1.0, 1.0};
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

imposta il colore verde per la componente diffuse e il colore bianco per la componente specular.

Attenuazione  
Nella realtà l'intensità della luce diminuisce all'aumentare della distanza. Anche nella nostra scena possiamo riprodurre questo effetto attraverso i parametri di attenuazione:
- GL_CONSTANT_ATTENUATION (Default = 1.0)
- GL_LINEAR_ATTENUATION    (Default = 0.0)
- GL_QUADRATIC_ATTENUATION (Default = 0.0)

Le conseguenze sull'intensità sono l'effetto della seguente funzione matematica:
AttenuationFactor = 1 /  kc + kl * d + kq * d^2
kc è la GL_CONSTANT_ATTENUATION
kl è la GL_LINEAR_ATTENUATION
kq è la GL_QUADRATIC_ATTENUATION
d è la distanza dalla fonte di luce

Light Model
OpenGL ci mette a disposizione la famiglia di funzioni glLightModel*() per settare alcuni parametri globali:
void glLightModel{if}(GLenum pname, TYPE param);
void glLightModel{if}v(GLenum pname, TYPE *param);

pname è il parametro da modificare
param è il valore del parametro

I parametri disponibili sono:
- GL_LIGHT_MODEL_AMBIENT
- GL_LIGHT_MODEL_LOCAL_VIEWER
- GL_LIGHT_MODEL_TWO_SIDE

Il primo parametro ci permette di settare un luce di ambiente globale, cioè che non proviene da nessuna delle 8 luci di OpenGL:

float lightmodel_ambient[4] = {0.4, 0.0, 0.0, 1.0};
glLightModelfv(lightmodel_ambient);

Gli oggetti nella scena in questo caso saranno influenzati da una tenue luce rossa e saranno quindi visibili anche se tutte le luci sono disabilitate.

GL_LIGHT_MODEL_LOCAL_VIEWER può avere come valore GL_FALSE o GL_TRUE. Si tratta di decidere in base a quale vettore calcolare gli effetti della luce speculare. Per capire il suo funzionamento dobbiamo dare una occhiatoa ai passi per calcolare la componente specualare su un oggetto che sono i seguenti:

s = lighdirection + viewpoint<
Normalize(s)

lighdirection è il vettore che parte dal vertice e punta verso la luce
viewpoint è il vettore che rappresenta la direzione dal quale proviene il nostro sguardo;
s è la somma di questi due vettori chee viene normalizzata

Ls = (s * n) ^ GL_SHININESS * specularlight * specularmaterial

Ls è il vettore dove verranno salvati i valori di colore da applicare al poligono
n è il vettore normale del vertice
GL_SHININESS è il valore che indica la brillantezza di un materiale
specularlight e specular material sono i valori della compoenente speculare rispettivamente di luce e materiale.

Il parametro GL_LIGHT_MODEL_LOCAL_VIEWER agisce sul vettore viewpoint, se è impostato a GL_TRUE verrà usata la reale posizione dell'osservatore e la riproduzione dell'effetto sarà più vicina alla realtà, se è uguale a GL_FALSE viene considerato il viewpoint infinito (0.0, 0.0, 1.0), ciò permette un aumento di prestazioni perchè questo valore è costante a scapito però della fedeltà.

Infine il parametro GL_LIGHT_MODEL_TWO_SIDE ci permette di abilitare l'illuminazione anche per le facce interne, cioè per la parte "dietro" dei nostri poligoni. Semplicemente OpenGL calcola l'inverso dei vettori normali indicati per la faccia frontale e calcola l'illuminazione. Naturalmente ciò provoca un rallentamento delle prestazioni poichè le operazioni devono essere seguite 2 volte per ogni poligono.  

Privacy Policy