This is an old revision of the document!
lab_list.h
, linia #include “lab/lab6/lab6.h”
.
In laboratoarele anterioare, am analizat modalitatea prin care se poate desena pe ecran geometria unor modele 3D. Pana in acest moment, culorile atribuite pixelilor in care a fost discretizata suprafata geometriei au fost alese artificial prin diferite abordari, precum interpolarea intre culorile varfurilor. Deoarece ochiul uman “vede” doar lumina, culoarea unui pixel in care a fost discretizata o suprafata, trebuie sa se calculeze pe baza influentei sursei lumina asupra punctului de pe suprafata. Pentru acest proces, se introduce in procesul de desenare, conceptul de sursa de lumina. Exista mai multe tipuri de astfel de surse:
Calcularea influentei unei sursa de lumina, de orice tip, asupra unui punct de pe o suprafata se realizeaza prin simularea procesului de transport al luminii de la pozitia sursei, prin reflexie pe suprafata, in pozitia punctului de calcul, la pozitia observatorului. Abordarea de simulare a acestui proces de simulare a transportului luminii poarta numele de model de iluminare sau de model de reflexie.
In general, in toate modelele de iluminare, influenta luminii asupra unui punct este impartita in 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
In situatia in care ne referim strict la transportul luminii de la suprafata la pozitia observatorului, aceasta componenta se poate aproxima printr-o distributie uniforma a imprastierii luminii de la suprafata in toate directiile, astfel ca se poate folosi o valoare constanta.
In general, este precalculata in aplicatiile grafice in timp real si se evita utilizarea pentru un numar mare de obiecte dinamice. Odata cu aparitia procesoarelor grafice ce au arhitecturi de ray-tracing, se folosesc astfel de tehnici pentru simularea transportului luminii de la o sursa, ce ajunge prin reflexii multiple pe mai multe suprafete, secvential, la pozitia observatorului.
Acestea se refera la transportul luminii de la pozitia unei surse, prin reflexie pe o suprafata la pozitia observatorului. Pentru comoditate, se imparte acest tip de reflexie in doua componente distincte:
Aceasta componenta se refera la cantitatea de lumina ce este imprastiata uniform prin reflexie pe o suprafata in toate directiile din jurul punctului de pe suprafata, 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
: