Laboratorul 06

Reamintire!!! Puteți prezenta rezolvările cerințelor de până la 2 laboratoare, în fiecare săptămână. De exemplu, puteți prezenta laboratorul curent și pe cel din săptămâna anterioară, în totalitate sau parțial, inclusiv punctajul pentru cerința bonus :) .

Pentru rezolvarea cerințelor din cadrul acestui laborator:

  1. Descărcați framwork-ul de laborator și copiați, din arhiva descărcată, directorul Lab6, în interiorul directorului gfx-framework-ppbg\src\lab din versiunea voastră de proiect.
  2. Adăugați în fișierul lab_list.h, linia #include “lab/lab6/lab6.h”.
  3. Folosiți din nou utilitarul CMake pentru a regenera proiectul. Pentru a vă reaminti procesul de realizare a setup-ului, puteți să reconsultați pagina dedicată acestui lucru.

Modele de iluminare

În laboratoarele anterioare, am analizat modalitatea prin care se poate desena pe ecran geometria unui model 3D. Până în acest moment, culorile atribuite pixelilor în care a fost discretizată suprafața geometriei au fost alese artificial prin diferite abordări, precum interpolarea între culorile vârfurilor. Deoarece ochiul uman “vede” doar lumină :) , culoarea unui pixel în care a fost discretizată o suprafață trebuie să se calculeze pe baza influenței unei surse de lumină asupra zonei din suprafață, reprezentată de pixel. Pentru acest proces, se introduce în procesul de desenare conceptul de sursă de lumină. Există mai multe tipuri de astfel de surse:

  • Sursă de lumina punctiformă, ce are o poziție în spațiu și împrăștie lumină uniform în toate direcțiile în jurul poziției.
  • Sursă de lumină de tip spot, ce are o poziție în spațiu și împrăștie lumină de la această poziție de-a lungul unei singure direcții. Acest tip de sursă este similar cu o lanternă.

Calcularea influenței iluminării ce provine de la o sursă de lumină, de orice tip, asupra unui punct de pe o suprafață, se realizează prin simularea procesului de transport al luminii de la poziția sursei, prin reflexie pe suprafață, în poziția punctului de pe suprafață, la poziția observatorului. O abordare care simulează acest proces de transport al luminii poartă numele de model de iluminare sau model de reflexie.

În general, în majoritatea modelelor de iluminare, influența luminii asupra unui punct este împărțită în 4 componente:

  • Componenta emisivă, reprezentată de lumina care este emisă direct de către suprafață. Este vorba aici de suprafețe ce emit lumină, precum suprafața unui bec sau a ecranului unui televizor.
  • Componenta de reflexie indirectă a luminii, ce se referă la procesul de transport al luminii, de la o sursă, prin reflexia pe mai multe suprafețe, secvențial, la poziția observatorului.
  • Componenta de reflexie difuză, ce se referă la transportul luminii de la o sursă, prin reflexia pe exact o singură suprafață, într-un punct, la poziția observatorului.
  • Componenta de reflexie oglindă, ce se referă, similar cu cea de reflexie difuză, la transportul luminii de la o sursă, prin reflexia pe o singură suprafață, într-un punct, la poziția observatorului.

Diferenta dintre ultimele două componente este dată de comportamentul de reflexie, respectiv o reflexie difuză și o reflexie oglindă.

Deoarece lumina este aditivă, cu cât mai mulți fotoni ajung în ochii nostri, cu atât lumina este mai “puternică” :), rezultatul final al influenței iluminării într-un punct este dat de suma celor 4 componente:

$$ iluminare = componentaEmisiva + componentaIndirecta + componentaDifuza + compoentaOglinda $$

Componenta emisivă

În situația în care ne referim strict la transportul luminii de la suprafața sursei la poziția observatorului, această componenta se poate aproxima printr-o distribuire uniformă a împrăștierii luminii de la suprafață în toate direcțiile, astfel că poate fi utilizată o singură valoare uniformă pentru toată suprafața.

În situația în care dorim să simulăm procesul de transport al luminii de la o suprafață ce emite lumină, prin reflexie pe o altă suprafață, la poziția observatorului, procesul devine complex. Există abordări pentru simularea lui, dar în general, în aplicații grafice în timp real se evită astfel de efecte vizuale.

Componenta indirectă a iluminării

În general, în aplicațiile grafice în timp real este precalculată și se evită utilizarea pentru un numar mare de obiecte dinamice. Odată cu apariția procesoarelor grafice ce au arhitecturi de ray-tracing, se folosesc astfel de tehnici pentru simularea transportului luminii de la o sursă, ce ajunge prin reflexii multiple pe mai multe suprafețe, secvențial, la poziția observatorului.

Componentele de reflexie directă

Acestea se referă la transportul luminii de la poziția unei surse, prin reflexie pe o suprafață, la poziția observatorului. Pentru comoditate, se împarte acest tip de reflexie în două componente distincte:

Componenta de reflexie difuză

Această componentă se referă la cantitatea de lumină ce este impraștiată uniform prin reflexie de pe o suprafață, în toate direcțiile din jurul punctului de pe suprafață, deasupra ei.

Componenta de reflexie oglindă

Această componentă se referă la cantitatea de lumină ce este reflectată de-a lungul direcției de reflexie simetrică față de direcția de incidență a luminii cu direcția vectorului normal. Această componentă este cunoscută sub numele de componenta speculară. Termenul speculum în limba latină se traduce în limba română cu termenul de oglindă :) .

Atenuarea intensității iluminării pe bază de distanță

În momentul în care o sursă de lumină se depărteaza de un obiect, prin procesul de difuzie, cantitatea de lumină emisă de la sursă rămâne constantă, dar obiectul primește o cantitate mai mică de lumină. Simularea acestui fenomen se realizează prin scăderea intenstității iluminării pe baza distanței dintre poziția sursei de lumină și poziția pentru care se calculează iluminarea.

Laborator

În practică, inclusiv în cadrul acestui laborator, se utilizează metode ce simulează fenomenele optice menționate mai sus.

Componenta emisivă

vec3 emissive_component = Ke; # GLSL

  • Ke – culoarea emisivă a obiectului

Componenta indirectă a iluminării

Este cunoscută în domeniul graficii pe calculator sub numele de componentă ambientală. Pentru a nu calcula tot transportul luminii cu toate reflexiile de pe suprafețe, se consideră că influența indirectă a iluminării este aceeași în toate punctele din scenă. Această aproximare obține rezultate satisfăcătoare pentru o parte din scenarii.

Avem astfel:

vec3 ambient_component = Ka * global_ambient_color; # GLSL

  • Ka – culoarea de reflexie ambientală a obiectului
  • global_ambient_color – culoarea ambientală a tuturor luminilor

Componenta difuză

În imaginea de mai jos se poate observa că un fascicul de lumină ce are o lățime de dimensiune $A$, este proiectat pe o suprafață, de-a lungul unei zone de dimensiune $B$. Se poate observa că în situația în care fasciculul este proiectat vertical pe suprafață, $B = A$.

Notăm cu $\alpha$ unghiul dintre vectorul $\vec{L}$, ce reprezintă direcția de la poziția punctului spre poziția sursei de lumină și vectorul normal, $\vec{N}$. Deoarece unghiul făcut de vectorul normal cu suprafața este 90 de grade, unghiul făcut de vectorul $\vec{L}$ cu suprafața este de $90-\alpha$. Pentru că suma unghiurilor unui triunghi este de 180 de grade și triunghiul din imagine este dreptunghic, rezultă că cel de-al treilea unghi din triunghi are dimensiunea $\alpha$. Astfel, rezultă că:

$$ cos(\alpha)=\frac{A}{B} \\ B=\frac{A}{cos(\alpha)} $$

Din acest motiv, se poate deduce că intensitatea de iluminare pentru orice punct de pe suprafață este $cos(\alpha)$. În situația în care $\alpha$ este de 0 grade, intensitatea de iluminare este $cos(0)=1$. Această abordare poartă numele de legea cosinusului a lui Lambert, propusă de Johann Heinrich Lambert în anul 1760.

În domeniul graficii pe calculator, această lege a cosinusului este utilizată pentru calculul componentei difuze a iluminării, ce se referă la lumina împrăștiată uniform în toate directiile. Mai exact, se consideră că lumina se împrăștie uniform în toate direcțiile cu intensitatea de iluminare dată de legea cosinusului a lui Lambert.

În practică, în loc de cosinus, se utilizează produsul scalar dintre $\vec{L}$ și $\vec{N}$, cu ambii vectori de lungime 1. Interpretarea geometrică a produsului scalar este:

$$ \vec{V_1}\cdot\vec{V_2}=\frac{cos(\angle(\vec{V_1},\vec{V_2}))}{\lVert\vec{V_1}\rVert\lVert\vec{V_2}\lVert} $$

În situația în care vectorii $\vec{V_1}$ și $\vec{V_2}$ sunt amândoi de lungime 1, expresia de mai sus este echivalentă cu:

$$ \vec{V_1}\cdot\vec{V_2}=cos(\angle(\vec{V_1},\vec{V_2})) $$

Pentru a obține un vector $\vec{V_u}$, de lungime 1, cunoscut sub numele de vector unitate, pe direcția și în sensul dat de un vector $\vec{V}$, putem aplica în limbajul GLSL:

vec3 Vu = normalize(V);

Astfel, calculul componentei difuze a iluminării este:

$$ componentaDifuza = K_d \cdot culoareLumina \cdot max(\vec{N}\cdot \vec{L}, 0) $$

În limbajul GLSL, expresia de mai sus se transcrie sub forma:

vec3 diffuse_component = Kd * culoareLumina * max (dot(N,L), 0); # GLSL

  • Kd - culoarea de reflexie difuză a obiectului
  • culoareLumina – culoarea luminii
  • $\vec{N}$ – vector normal (normalizat)
  • $\vec{L}$ – vectorul direcției luminii incidente (normalizat)
  • $max(\vec{N}\cdot \vec{L}, 0)$ – produsul scalar $\vec{N}\cdot \vec{L}$ reprezintă măsura unghiului dintre acești 2 vectori; astfel, dacă $\alpha$ 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ă

Pentru calcularea componentei speculare, vom folosi modelul propus de Bui Tuong Phong în anul 1973:

$$ componentaSpeculara = K_s \cdot culoareLumina \cdot primesteLumina \cdot (max(\vec{V}\cdot \vec{R}, 0))^n $$

În limbajul GLSL, expresia de mai sus se transcrie sub forma:

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

  • Ks - culoarea speculară de reflexie a obiectului
  • $\vec{V}$ – vectorul direcției de vizualizare (normalizat)
  • $\vec{R}$ – vectorul direcției luminii reflectate (normalizat)
  • n – coeficientul de strălucire (shininess) al materialului
  • primesteLumina – 1 dacă $\vec{N}\cdot \vec{L}$ este mai mare decât 0; sau 0 în caz contrar

Componenta speculară reprezintă lumina reflectată de suprafața obiectului numai în jurul direcției $\vec{R}$. Acest vector se obține prin:

vec3 R = reflect (-L, N) # GLSL

  • Este necesar să se utilizeze -L, deoarece reflect() are primul parametru vectorul incident, care intră în suprafață, nu cel care iese din ea așa cum este reprezentat în imaginea de mai sus.

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

După 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.

Atenuarea intensității iluminarii

Factorul de atenuare a intensității iluminării pe bază de distanță se aplică doar componentei difuze și speculare:

vec3 illumination = emissive_component + ambient_component
                    + attenuation_factor * ( diffuse_component + specular_component); # GLSL

  • attenuation_factor = $1/(d^2+1)$ este o funcție de atenuare
  • $d$ este distanța de la poziția sursei de lumină la poziția punctului de pe suprafață pentru care se calculează iluminarea

Sursa de lumină de tip spot

Pentru a simula o sursă de lumină precum o lanternă, ce împrăștie lumină de-a lungul unei directii de iluminare, se introduc câteva informații suplimentare:

  • Vectorul de direcție a iluminării sursei de lumină
  • Unghiul de tăiere a iluminării, ce controlează deschiderea conului de lumină
  • Un model de atenuare a intensității iluminări pe baza unghiului de tăiere

În domeniul graficii pe calculator, o astfel de sursă de lumină poartă numele de sursă de tip spot.

Un punct se află în conul de lumină al unei surse de tip spot dacă condiția următoare este îndepilită:

float cos_theta_angle = dot(-L, light_direction);
float cos_phi_angle = cos(phi_angle);
 
if (cos_theta_angle > cos_phi_angle )
{
	// fragmentul este iluminat, astfel ca se calculeaza valoarea luminii conform modelului Phong
	// se calculeaza atenuarea luminii
}

Pentru a simula corect iluminarea de tip spot, este nevoie să tratăm și atenuarea iluminării corespunzătoare apropierii unghiului de tăiere. Putem astfel sa utilizăm un model de atenuare pătratică ce oferă un rezultat convingător.

float spot_linear_att_factor = (cos_theta_angle - cos_phi_angle) / (1.0f - cos_phi_angle);
float quadratic_spot_light_att_factor = pow(spot_linear_att_factor, 2);

Biblioteca GLM

Matrici de transformare

Biblioteca GLM ne pune la dispoziție metode de construcție a matricilor celor 3 tipuri de transformări analizate: translație, modificare de scară și rotație. Următoarele 2 lanțuri de transformări sunt identice:

glm::mat4 model = glm::mat4(1);
model = glm::translate(model, glm::vec3(-5, 1.5f, 1));
model = glm::rotate(model, glm::radians(60.0f), glm::vec3(0, 1, 0));
model = glm::scale(model, glm::vec3(0.1f));
glm::mat4 model = glm::mat4(1);
model *= transform3D::Translate(glm::vec3(-5, 1.5f, 1));
model *= transform3D::RotateOY(glm::radians(60.0f));
model *= trasnform3D::Scale(glm::vec3(0.1f));

Aplicarea matricilor de transformare

Dacă dorim să rotim un vector cu 30 de grade față de axa OY, putem utiliza construcția matricii pentru transformarea de rotație, pusă la dispoziție de biblioteca glm, după cum urmează:

glm::vec3 v = glm::vec3(1, 0, 0);
 
glm::mat4 rotation = glm::rotate(glm::mat4(1), glm::radians(30.0f), glm::vec3(0, 1, 0));
 
// First option
v = glm::mat3(rotation) * v;
 
// Second option
v = glm::vec3(rotation * glm::vec4(v, 1.0f));

Transmiterea de vectori în atribute de tip uniform

Pentru a transmite un vector de obiecte ce au tipuri de date din biblioteca GLM într-un atribut de tip uniform, la un shader, se poate utiliza:

glm::vec3 light_positions[10];
 
glUniform3fv(location, 10, glm::value_ptr(light_positions[0]));

Detalii de implementare

  • În acest laborator, nu se va utiliza componenta emisivă.
  • Vectorul normal al fiecărui vârf se regăsește în datele conținute de fișierul modelului încărcat și se primește în vertex shader într-un atribut de intrare spefic vârfului:
    layout(location = 1) in vec3 v_normal;
  • Calculele de iluminare se vor face în spațiul lumii, astfel că poziția și normala vor trebui transformate din spatiul obiectului, în care se află inițial, în spatiul lumii. Aceste calcule se realizează în vertex shader și noua poziție și vector normal se transmit spre fragment shader. Calculul se poate face astfel:
    • pentru poziție:
      vec3 world_position = (model_matrix * vec4(v_position,1)).xyz;
    • pentru vectorul normal:
      vec3 world_normal = normalize( mat3(model_matrix) * v_normal );
  • Vectorul normal trebuie normalizat in fragment shader, deoarece după procesul de interpolare, lungimea lui nu se pastrează:
    vec3 N = normalize( world_normal );
  • Vectorul direcției luminii L:
    vec3 L = normalize( light_position - world_position );
  • Vectorul direcției din care privește observatorul V:
    vec3 V = normalize( eye_position - world_position );

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

  • normalize(V) – normalizează vectorul V
  • normalize(V1+V2) – normalizează vectorul obținut prin V1+V2
  • normalize(P1-P2) - returnează un vector de direcție normalizat între punctele P1 și P2
  • dot(V1,V2) – calculează produsul scalar dintre V1 și V2
  • pow(a, shininess) – calculează a la puterea shininess
  • max(a,b) – returnează maximul dintre a și b
  • distance(P1,P2) – returnează distanța euclidiană dintre punctele P1 și P2
  • reflect(V,N) - calculează vectorul de reflexie pornind de la incidenta V și normala N

Cerințe laborator

Prin apăsarea tastelor W, A, S, D, E și Q, puteți controla poziția unei surse de lumină. Prin apăsarea tastei F, puteți interschimba între controlul a două surse de lumină diferite, una punctiformă și cealaltă de tip spot.

  1. 0.05p - Trimiteți toate informațiile, necesare calculării iluminării, în atribute de tip uniform.
  2. 0.15p - Implementați calculul de iluminare în fișierele sursă ale programelor de tip shader:
    • Completați în fișierul VertexShader.glsl calculul poziției vârfului și a vectorului normal în spațiul lumii și transmiteți-le spre fragment shader.
    • Completați în fișierul FragmentShader.glsl:
      • Calculul componentei difuze și speculare a iluminării unei surse de lumină
      • Calculul factorului de atenuare a iluminării pe bază de distanță
      • Calculul final de obținere a iluminării prin combinarea componentei difuze și speculare, a factorului de atenuare și a culorii iluminării
      • Calculul componentei ambientale a iluminării globale
      • Până în acest punct, rezultatul pe care ar trebui să îl obțineti este următorul. Culoarea fiecărei lumini este aleasă aleatoriu la începutul fiecărei execuții a aplicatiei grafice
  3. 0.05p - Implementați calculul de iluminare pentru sursele de lumina de tip spot:
    • Completati în fisierul FragmentShader.glsl:
      • Calculul factorului de atenuare specific unei surse de lumină de tip spot
      • Calculul final de obținere a iluminării prin combinarea componentei difuze și speculare, a factorului de atenuare pe bază de distanță, a factorului de atenuare specific unei surse de lumină de tip spot și a culorii iluminării
  4. 0.05p - Pentru sursa de lumina de tip spot ce poate fi controlată de la tastatură, prin apăsarea unor taste, modificați direcția de iluminare și unghiul. Direcția de iluminare trebuie să se poata roti față de axa OX și OZ, în ambele sensuri, iar unghiul trebuie să se poată mări și micșora.
ppbg/laboratoare/06.txt · Last modified: 2024/01/10 18:05 by andrei.lambru
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