Laboratorul 07

Iluminare folosind GLSL

Lumina este un factor foarte important în redarea cât mai realistă a unei scene 3D. Împreună cu proprietățile de material ale unui obiect, lumina determină modalitatea în care obiectul este afișat în scena 3D.

Există mai multe modele empirice pentru calculul reflexiei luminii într-un punct al unei suprafețe: Phong (1975), Blinn-Phong (1977), Oren-Nayar (1994), Cook-Torrance (1981), Lambert (1760), etc (la curs veți discuta despre modelul Lambert și despre modelul Phong).

De asemenea, există mai multe modele de shading, care specifică metoda de implementare a modelului de calcul al reflexiei luminii. Mai exact, modelul de shading specifică unde se evaluează modelul de reflexie. Dacă vrem să calculăm iluminarea pentru o suprafață poligonală:

  • în modelul de shading Lambert, se calculează o singură culoare pentru un poligon al suprafeței
  • în modelul de shading Gouraud (1971), se calculează câte o culoare pentru fiecare vârf al unui poligon. Apoi, culorile fragmentelor poligonului se calculează prin interpolare între vârfuri (interpolarea liniară a culorilor vârfurilor, pentru fragmentele de pe laturi și interpolare liniară între culorile capetelor fiecărui segment interior, pentru fragmentele interioare poligonului). Calcularea culorilor vârfurilor se poate efectua în vertex shader.
  • în modelul de shading Phong (1975), se calculează câte o normală pentru fiecare vârf al unui poligon. Apoi, pentru fiecare fragment se determină o normală prin interpolare între normalele din vârfuri. Astfel, se calculează o culoare pentru fiecare fragment al unui poligon (în fragment shader)

Figura 1. Diferite modele de shading: Lambert (o culoare per primitivă), Gouraud (o culoare per vârf), Phong (o culoare per fragment)

În acest laborator se va discuta modelul de shading Gouraud peste modelul de reflexie Phong.

Modelul Phong pentru calculul reflexiei luminii

Ca model de reflexie vom prezenta în continuare un model care extinde modelul de reflexie Phong și care conține toate cele 4 componente care pot fi folosite pentru a calcula iluminarea. Pentru a obține astfel culoarea într-un punct al unei suprafețe vom avea următoarele componente :

  • Componenta emisivă
  • Componenta ambientală
  • Componenta difuză
  • Componenta speculară

Contribuția fiecărei componente este calculată ca o combinație dintre proprietățile de material ale obiectului (factorul de strălucire, culoarea materialului) și proprietățile sursei de lumină (culoarea sursei de lumină, poziția sursei de lumină).

Astfel, culoarea finală a unui punct aparținând unei suprafețe este:

     culoare = emisiva + ambientala + difuza + speculara

În cele ce urmează prezentăm pe scurt ce reprezintă cele 4 componente și cum pot fi calculate.

Componenta emisivă

Aceasta reprezintă lumina emisa de un obiect și nu tine cont de nicio sursă de lumină. Dacă un obiect care are o anumită culoare emisivă s-ar afla într-o scenă complet întunecată atunci el ar apărea exact cu această culoare.

O utilizare des întâlnită pentru componenta emisivă este aceea de a simula strălucirea unui obiect.

Avem astfel:

     emisiva = Ke

  • Ke – culoarea emisivă a materialului

Componenta ambientală

Aceasta reprezintă lumina reflectată de catre obiectele din scenă de atât de multe ori încât pare să vină de peste tot.

Astfel, lumina ambientală nu vine dintr-o direcție anume, apărând ca și cum ar veni din toate direcțiile. Din această cauză, componenta ambientală este independentă de poziția sursei de lumină.

Componenta ambientală depinde de culoarea de material ambientală a suprafeței obiectului și de culoarea ambientală a luminii.

Similar componentei emisive, componenta ambientală este o constantă (se poate extinde modelul atribuind fiecărei lumini din scenă o culoare ambientală).

Avem astfel:

     ambientala = Ka * culoareaAmbientalaGlobala

  • Ka – constanta de reflexie ambientală a materialului
  • culoareaAmbientalaGlobala – culoarea ambientală a luminii

Componenta difuză

Aceasta reprezintă lumina reflectată de suprafața obiectului în mod egal în toate direcțiile.

Cantitatea de lumină reflectată este proporțională cu unghiul de incidență al razei de lumină cu suprafața obiectului.

Avem astfel:

     difuză = Kd * culoareLumina * max (dot(N,L), 0)

  • Kd - constanta de reflexie difuza a materialului
  • culoareLumina – culoarea difuză a luminii
  • N – normala la suprafață (normalizată)
  • L – vectorul direcției luminii incidente (normalizat)
  • $max(N\cdot L, 0)$ – produsul scalar $N\cdot L$ reprezintă măsura unghiului dintre acești 2 vectori; astfel, dacă $i$ este mai mare decât $\pi/2$ valoarea produsului scalar va fi mai mică decât 0, acest lucru însemnând că suprafața nu primește lumină ( sursa de lumină se află în spatele suprafeței ) și de aici și formula care asigură că în acest caz suprafața nu primește lumină difuză

Componenta speculară

Un reflector perfect, de exemplu o oglindă, reflectă lumina numai într-o singură direcție $R$, care este simetrică cu $L$ față de normala la suprafață. Prin urmare, doar un observator situat exact pe direcția respectivă va percepe raza reflectată.

Componenta speculară reprezintă lumina reflectată de suprafața obiectului numai în jurul acestei direcții, $R$.

În modelul Phong se aproximează scăderea rapidă a intensității luminii reflectate atunci când $\alpha$ crește prin $cos^n \alpha$, unde $n$ este exponentul de reflexie speculară al materialului (shininess).

Astfel avem: $speculară = K_s \cdot culoareLumina \cdot primesteLumina \cdot (max(V\cdot R, 0))^n$

     speculară = Ks * culoareLumina * primesteLumina * pow(max(dot(V, R), 0), n) # GLSL

O altă formulare a modelului Phong se bazează pe vectorul median, notat cu $H$. El face unghiuri egale cu $L$ și cu $V$. Dacă suprafața ar fi orientată astfel încât normala sa să aibă direcția lui $H$, atunci observatorul ar percepe lumina speculară maximă (deoarece ar fi pe direcția razei reflectate specular).

Termenul care exprimă reflexia speculară este în acest caz: $(N_u \cdot H_u)^n$

     pow(dot(Nu, Hu), n) # GLSL

  • $H_u = (L_u + V_u)$ (normalizat)

Atunci când sursa de lumină și observatorul sunt la infinit, utilizarea termenului $N_u\cdot H_u$ este avantajoasă deoarece $H_u$ este constant.

Dupa cum se observă, față de celelalte 3 componente, componenta speculară depinde și de poziția observatorului. dacă observatorul nu se află într-o poziție unde poate vedea razele reflectate, atunci nu va vedea reflexie speculară pentru zona respectivă. De asemenea, nu va vedea reflexie speculară dacă lumina se află în spatele suprafeței.

Ținând cont de toate acestea, avem pentru componenta speculară următoarea formulă: $speculara = K_s \cdot culoareLumina \cdot primesteLumina \cdot (max(N\cdot H, 0)^n $

     speculara = Ks * culoareLumina * primesteLumina * pow(max(dot(N, H), 0), n) # GLSL

  • Ks - constanta speculara de reflexie a materialului
  • culoareLumina – culoarea speculară a luminii
  • N – normala la suprafață (normalizată)
  • L – vectorul direcției luminii incidente (normalizat)
  • H – vectorul median (normalizat)
  • primesteLumina – 1 dacă $N\cdot L$ este mai mare decat 0; sau 0 în caz contrar

Atenuarea intensității luminii

Atunci când sursa de lumină punctiformă este suficient de îndepărtată de obiectele scenei vizualizate, vectorul $L$ este același în orice punct. Sursa de lumină este numită în acest caz direcțională. Aplicând modelul pentru vizualizarea a două suprafețe paralele construite din același material, se va obține o aceeași intensitate (unghiul dintre $L$ și normală este același pentru cele două suprafețe). Dacă proiecțiile suprafețelor se suprapun în imagine, atunci ele nu se vor distinge. Aceasta deoarece în model nu se ține cont de faptul că intensitatea luminii descrește proporțional cu inversul pătratului distanței de la sursa de lumină la obiect. Deci, obiectele mai îndepărtate de sursă sunt mai slab luminate. O posibilă corecție a modelului, care poate fi aplicată pentru surse poziționale (la distanță finită de scenă) este:

   culoareObiect = emisiva + ambientala + factorAtenuare * ( difuza + speculara )

  • factorAtenuare = $1/d^2$ este o funcție de atenuare
  • $d$ este distanța de la sursă la punctul de pe suprafață considerat

Corecția de mai sus nu satisface cazurile în care sursa este foarte îndepărtată. De asemenea, dacă sursa este la distanță foarte mică de scenă, intensitățile obținute pentru două suprafețe cu același unghi $i$, între $L$ și $N$, vor fi mult diferite.

O aproximare mai bună este următoarea: factorAtenuare = $1/(K_c + K_l\cdot d + K_q\cdot d^2)$

  • $K_c$ - factorul de atenuare constant
  • $K_l$ - factorul de atenuare liniar
  • $K_q$ - factorul de atenuare patratic

Detalii de implementare

Pentru simplitate, în cadrul laboratorului vom implementa modelul de shading Gouraud (în vertex shader):

  • Se vor calcula practic doar componentele difuze și speculare așa cum au fost prezentate anterior; componenta emisivă nu va fi folosită iar calculul componentei ambientale va fi simplificat astfel încât să nu mai trebuiască trimis nimic din program către shader (mai multe detalii la punctul 3).
  • Vom folosi ca materiale pentru obiecte doar culoarea de material difuză și speculară (transmise din program către shader) : Ks și Kd.
  • În shader vom aproxima lumina ambientală cu o culoareAmbientalaGlobala care va fi o constantă în shader, iar în loc de Ka (constanta de material ambientală a obiectului) vom folosi Kd (constanta de material difuză a obiectului).
  • Culoarea luminii (difuză și speculară) va fi albă, deci culoareLumina va fi 1 și nu va mai fi necesar să fie folosită la înmulțirile din formulele de calcul pentru componentele difuză și speculară.
  • Calculele de iluminare se vor face în world space, deci înainte de a fi folosite, poziția și normala vor trebui aduse din object space în world space. Acest lucru se poate face astfel:
    • pentru position:
      vec3 world_pos = (model_matrix * vec4(v_position,1)).xyz;
    • pentru normala:
      vec3 world_normal = normalize( mat3(model_matrix) * v_normal );
  • Vectorul direcției luminii L:
    vec3 L = normalize( light_position - world_pos );
  • Vectorul direcției din care priveste observatorul V:
    vec3 V = normalize( eye_position - world_pos );
  • Vectorul median H:
    vec3 H = normalize( L + V );

Funcții GLSL utile care pot fi folosite pentru implementarea modelului de iluminare

  • normalize(V) – normalizează vectorul V
  • normalize(L+V) – normalizează vectorul obținut prin L+V
  • normalize(X1-X2) - returnează un vector de direcție normalizat având două puncte X1 și X2
  • dot(N,L) – calculează produsul scalar dintre N și L
  • pow(L, shininess) – calculează L la puterea shininess
  • max(N,V) – returnează maximul dintre N și V
  • distance(P1,P2) – returnează distanța euclidiană dintre punctele P1 și P2
  • reflect(L,N) - calculează vectorul de reflexie pornind de la incidenta L și normala N

Cerințe laborator

tasta F5 - reîncarcă shaderele în timpul rularii aplicatiei. Nu este nevoie să opriți aplicația întrucât shaderele sunt compilate și rulate de către placa video și nu au legătură cu codul sursă C++ propriu zis.

  1. Completați funcția RenderSimpleMesh astfel încât să trimiteți corect valorile uniforme către Shader:
    • poziția luminii
    • poziția camerei
    • proprietățile de material (Kd, Ks, shininess, culoare obiect)
  2. Implementați iluminarea în Vertex Shader
    • Vectorii N, V, L și poziția în spațiul global
    • Componenta ambientală
    • Componenta difuză
    • Componenta speculară (atât în varianta de bază cât și folosind vectorul median)
    • Factor de atenuare
    • Culoarea finală
  3. Completați fragment shader-ul astfel încât să aplicați iluminarea calculată în Vertex Shader
  4. Colorați sfera și planul din scena (de ex: sfera - albastru, planul - gri)
egc/laboratoare/07.txt · Last modified: 2019/11/20 09:03 by anca.morar
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0