This is an old revision of the document!
lab_list.h
, linia #include “lab/lab6/lab6.h”
.
În laboratoarele anterioare, am analizat modalitatea prin care se poate desena pe ecran geometria unor modele 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 sursei lumină asupra punctului de pe suprafață. Pentru acest proces, se introduce în procesul de desenare, conceptul de sursă de lumină. Există mai multe tipuri de astfel de surse:
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:
Diferenta dintre ultimele doua componente este data de comportamentul de reflexie, respectiv o reflexie difuza si o reflexie oglinda.
Deoarece lumina este aditiva, cu cat mai multi fotoni ajung in ochii nostri, cu atat lumina este mai “puternica” :), rezultatul final al influentei iluminarii intr-un punct este dat de suma celor 4 componente:
influenta_finala = componenta_emisiva + componenta_indirecta + componenta_difuza + componenta_stralucire; # GLSL
Î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ă se poate aproxima printr-o singură valoare uniformă pentru toată suprafața.
Î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.
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:
Această componentă se refera la cantitatea de lumină ce este impraștiată uniform prin reflexie pe o suprafață, în toate direcțiile din jurul punctului de pe suprafață, deasupra ei.
Aceasta componenta se refera la cantitatea de lumina ce este reflectata de-alungul directiei de reflexie simetrica fata de directia de incidenta a luminii cu directia vectorului normal. Aceasta componenta este cunoscuta sub numele de componenta speculara. Termenul speculum in limba latina se traduce in limba romana cu termenul de oglinda :) .
In momentul in care sursa de lumina a un fascicul difuz se departeaza de un obiect, prin procesul de difuzie, cantitatea de lumina emisa se pastreaza, dar obiectul primeste o cantitate mai mica de lumina. Calculul acestui fenomen se realizeaza pe baza de distanta fata pozitia sursei de lumina si pozitia pentru care se calculeaza iluminarea.
In practica, inclusiv in cadrul acestui laborator, se utilizeaza simulari ale fenomenelor optice.
componenta_emisiva = Ke; # GLSL
Este cunoscuta in practica sub numele de componenta ambientala. Pentru a nu calcula tot transportul luminii cu toate reflexiile de pe suprafete, se considera ca influenta indirecta a iluminarii este aceeasi in toate punctele din scena. Aceasta aproximare obtine rezultate satisfacatoare pentru o parte din scenarii.
Avem astfel:
ambient_component = Ka * global_ambient_color; # GLSL
In imaginea de mai jos se poate observa ca un fascicul de lumina ce are o latime de dimensiune A, este proiectat pe suprafata de-alungul unei zone de dimensiune B. Se poate ca in situatia in care fasciculul este proiectat vertical pe suprafata, B = A.
Notam cu alpha unghiul dintre vectorul spre directia sursei de lumina, L, si vectorul normal, N. Deoarece unghiul facut cu normala este 90 de grade, unghiul fata de vectorul L, realizat cu suprafata este de 90-alpha. Pentru ca suma unghiurilor unui triunghi este de 180 si triunghiul este dreptunghic, rezulta ca cel de-al treilea unghi din triunghi are dimensiunea alpha. Astfel, rezulta:
$$ cos(\alpha)=\frac{A}{B} \\ B=\frac{A}{cos(\alpha)} $$
Din acest motiv, se poate deduce ca intensitatea de iluminare pentru orice punct de pe suprafata este cos(alpha). In situatia in care alpha este 0 grade, intensitatea de iluminare este cos(0) = 1. Aceasta abordare poarta numele de legea cosinusului a lui Lambert, propusa de Johann Heinrich Lambert in 1760.
In grafica pe calculator, aceasta lege a cosinusului este utilizata pentru calculul de reflexie a uniforma in toate directiile. Mai exact, se considera ca lumina se imprastie uniform in toate directiile cu intensitatea de iluminare data de legea cosinusului a lui Lambert.
In practica, in loc de cosinus se utilizeaza produsul scalar dintre L si N, cu L si N, ambii de lungime 1. Interpretarea geometrica 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} $$
In situatia in care vectorii V1 si V2 sunt de lungime 1 amandoi, expresia de mai sus este echivalenta cu:
$$ \vec{V_1}\cdot\vec{V_2}=cos(\angle(\vec{V_1},\vec{V_2})) $$
vec3 Vu = normalize(V);
Astfel, calculul componentei difuze a iluminarii este:
$$ componentaDifuza = K_d \cdot culoareLumina \cdot max(\vec{N}\cdot \vec{L}, 0) $$
In limbajul glsl, expresia de mai sus se transcrie sub forma:
vec3 diffuse_component = Kd * culoareLumina * max (dot(N,L), 0); # GLSL
Pentru calcularea componentei speculare, vom folosi modelul propus de Bui Tuong Phong in 1973:
$$ componentaSpeculara = K_s \cdot culoareLumina \cdot primesteLumina \cdot (max(\vec{V}\cdot \vec{R}, 0))^n $$
vec3 specular_component = Ks * culoareLumina * primesteLumina * pow(max(dot(V, R), 0), n) # GLSL
Componenta speculară reprezintă lumina reflectată de suprafața obiectului numai în jurul acestei direcții, $\vec{R}$. Acest vector se obține prin:
vec3 R = reflect (-L, N) # GLSL
reflect()
are primul parametru vectorul incident care intră în suprafață, nu cel care iese din ea așa cum este reprezentat în figură
Î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.
Factorul de atenuare a intensitatii iluminarii pe baza de distanta se aplica doar componentelor difuza si speculara:
vec3 illumination = emissive_component + ambient_component + attenuation_factor * ( diffuse_component + specular_component); # GLSL
Pentru a simula
Nu toate sursele de lumina sunt punctiforme. Daca dorim sa implementam iluminarea folosind o sursa de lumina de tip spot trebuie sa tinem cont de o serie de constrangeri
Asa cum se poate vedea si in poza pentru a implementa o sursa de lumina de tip spot avem nevoie de urmatorii parametri aditionali:
Astfel, punctul P se afla in conul de lumina (primeste lumina) daca conditia urmatoare este indepilita:
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 sa tratam si atenuarea luminii corespunzatoare apropierii unghiului de cut-off. Putem astfel sa utilizam un model de atenuare patratica ce ofera un rezultat convingator.
float cos_theta_angle = dot(-L, light_direction); float cos_phi_angle = cos(phi_angle); // Quadratic attenuation 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 ne pune la dispozitie constructia de matrici pentru cele 3 tipuri de transformari analizate: translatie, modificare de scara si rotatie. Urmatoarele 2 lanturi de transformari 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));
vec3 world_position = (model_matrix * vec4(v_position,1)).xyz;
vec3 world_normal = normalize( mat3(model_matrix) * v_normal );
vec3 N = normalize( world_normal );
vec3 L = normalize( light_position - world_position );
vec3 V = normalize( eye_position - world_position );
VertexShader.glsl
calculul pozitiei varfului si a vectorului normal in spatiul lumii si transmiteti-le spre fragment shader.FragmentShader.glsl
:FragmentShader.glsl
: