This is an old revision of the document!
În acest laborator, vom introduce atât elemente noi de OpenGL, cât și o abordare pentru calcularea umbrelor realizate de iluminarea unei surse de lumină de tip spot. Metoda prezentată aici se numeste metoda mapării umbrelor, întâlnită în engleză sub numele de shadow mapping, ce a fost prezentată în cadrul cursului de EGC din anul III. Va recomandăm să revizualizați secțiunea despre texturi din pagina de Recapitulare EGC. Această recomandare reapare mai jos în pagină, acolo unde noțiunile de la EGC sunt explicit necesare.
Prima parte a laboratorului se concentrează doar pe descrierea obiectelor de tip framebuffer. Partea a doua reia sumar metoda mapării umbrelor și oferă mai multe detalii doar despre pașii tehnicii, ce țin de utilizarea obiectelor de tip framebuffer.
Redarea scenei în fereastra de desenare se realizează, de fapt prin redarea scenei într-o textură specială, ce este afișată ulterior în fereastră. API-ul grafic OpenGL nu permite desenarea direct într-o textură, ci impune utilizarea unui obiect suplimentar, numit buffer de cadru sau framebuffer. Acest obiect conține:
Pentru a crea un obiect de tip framebuffer, putem folosi directiva OpenGL:
unsigned int framebuffer_object; glGenFramebuffers(1, &framebuffer_object);
Fereastra de desenare deține un framebuffer implicit, ce este creat automat în framework-ul de laborator prin intermediul librăriei GLFW. Astfel, orice redare a scenei se realizează inițial în texturile acestui framebuffer. Pentru a desena în texturile obiectului de tip framebuffer creat de noi mai sus sau pentru a modifica acest obiect, este necesară legarea acestuia la banda grafică după cum urmează:
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
În momentul de față avem un framebuffer nou, gol, ce nu conține nicio textură de culoare sau de adancime, dar care este legat la banda grafică. Putem lega texturi la framebuffer și atunci când vom reda o scenă și acest framebuffer va fi legat la banda grafică, procesul de desenare va scrie rezultatele în texturile obiectului legat de noi.
Reamintim că pentru a crea o textură cu format de culoare, folosim următoarele directive:
unsigned int color_texture; glGenTextures(1, &color_texture); glBindTexture(GL_TEXTURE_2D, color_texture); // Pixelii din interiorul texturii au formatul RGB glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
Pentru a atașa textura cu format de culoare (R, RG, RGB, RGBA), creată mai sus, la framebuffer, folosim:
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+pct_atasare, color_texture, 0);
Astfel, atașăm textura color_texture
la obiectul de tip framebuffer legat la banda grafică pe punctul de atașare pct_atasare
. Valoarea 0
de la final ne spune că atașăm primul nivel din mipmap (rezoluția maximă). Dupa cum se poate observa, obiectele de tip framebuffer au puncte de atașare ce foarte similare din punct de vedere conceptual cu ideea de pipe folosită la definirea informației la nivel de vertex. În API-ul grafic OpenGL, acest tip de proiectare este foarte des folosit. Dacă atașăm o textură la un punct de legare pe care deja este legată o altă textură, legătura veche se va pierde și va rămâne doar cea noua.
Este important să observăm mai sus că punctul de atasare este de tip GL_COLOR_ATTACHMENT
. Mai există un alt tip de punct de atașare, cu un singur punct (unic!) folosit pentru textura de adâncime, numit GL_DEPTH_ATTACHEMENT
. Pentru a lega o textură de adâncime, putem folosi:
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_texture, 0);
Crearea unei texturi ce conține informație de adâncime este similară cu procesul de creare a unei texturi ce conține informație de culoare, descris mai sus, cu excepția formatului definit în directiva glTexImage2D
:
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
Pentru a putea folosi obiectul de tip framebuffer creat, mai sunt încă două etapa de efectuat: setarea texturile cu format de culoare pe care se desenează și verificarea statusului de creare a obiectului.
Pentru a seta texturile de desenare folosim:
std::vector<GLenum> draw_textures; draw_textures.push_back(GL_COLOR_ATTACHMENT0+attachment_index_color_texture); glDrawBuffers(drawbuffers.size(),&draw_textures[0]);
Pratic, cu directiva glDrawBuffers
, setăm care sunt texturile în care se desenează. În exemplul de mai sus, avem o singură textură atașată pe atașamentul de culoare cu numărul 0, pe care o adaugăm într-un vector pe poziția 0. Dacă avem obiectul de tip framebuffer în cauză legat la banda grafică și în fragment shader-ul utilizat pentru desenarea în acest framebuffer, avem codul de mai jos, deoarece în out_color scriem un pixel roșu, toți pixelii texturii de culoare vor fi roșii.
layout(location = 0) out vec4 out_color; void main() { out_color = vec4(1, 0, 0, 1); }
După cum s-a menționat mai sus, putem avea mai multe texturi cu format de culoare atașate unui framebuffer. Acest mecanism va fi prezentat mai în detaliu în laboratorul 6. Dar, ca o previzualizare a informației din acel laborator, codul de mai jos scrie în 4 texturi diferite, de tipuri diferite și se poate observa că textura din atașamentul de culoare numarul 0 este complet roșie, iar cea din atașamentul de culoare numărul 1 este complet albastră.
layout(location = 0) out vec4 out_color; layout(location = 1) out vec3 color2; layout(location = 2) out int int_texture; layout(location = 3) out float float_texture; void main() { out_color = vec4(1, 0, 0, 1); color2 = vec3(0, 0, 1); int_texture = 1; float_texture = 3.14; }
În acest exemplu, pe atașamentul de culoare numărul 0 merge ce e scris în out_color, pe atașamentul de culoare cu numărul 1 merge ce este scris în color2, pe atașamentul de culoare cu numărul 2 merge ce este scris în int_texture, iar pe atașamentul de culoare cu numărul 3 merge ce este scris în float_texture.
Ultima etapă necesară, înainte de folosirea obiectului de tip framebuffer, este testarea corectitudinii creării sale:
glCheckFramebufferStatus(GL_FRAMEBUFFER);
În momentul în care se dorește din nou redarea scenei în texturile obiectului de tip framebuffer implicit, putem folosi:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Noi vom utiliza metoda maparii umbrelor pentru a obtine efectul de umbre ale iluminarii realizate de catre o sursa de lumina de tip spot. Pentru a revizualiza mai multe detalii despre acest tip de sursa de lumina, va rugam sa consultati sectiunea aferenta din pagina de Recapitulare EGC.
Metoda contine 2 pasi:
Pentru a verifica daca pentru un fragment obtinut prin redarea scenei din perspectiva observatorului trebuie sa se calculeze intensitatea iluminarii sau se afla in umbra, putem folosi textura cu format de adancime din obiectul de tip framebuffer obtinut la pasul 1. Se verifica daca distanta dintre pozitia in spatiul lume a fragmentului este aceeasi cu cea din textura cu format de adancime de la pasul 1, cand pozitia fragmentului este proiectata in aceasta textura. Un exemplu poate fi vazut in figura de mai jos, unde pixelul marcat cu rosu in panoul a) este proiectat in pixelul marcat cu rosu din textura de adancime din panoul b). Se poate observa ca fragmentul marcat cu rosu in panoul a) a fost obtinut prin rasterizarea modelului ce descrie pamantul, dar proiectia lui pe textura de adancime, intalneste un pixel rezultat in urma rasterizarii modelului de bambus.
In situatia in care distanta este mai mare decat cea din textura cu format de adancime, inseamna ca in aceasta textura este desenat un obiect ce se afla mai aproape de sursa de lumina si astfel umbreste fragmentul pentru care calculam intensitatea iluminarii. Acest exemplu este chiar in figura de mai jos, unde pozitia fragmentului din panoul a), de pe teren, este mai departe de sursa de lumina fata de fragmentul ce se regaseste la pozitia proiectiei lui in textura cu format de adancime, unde se afla un fragment din frunza bambusului. O observatie importanta de care trebuie sa se tina cont este ca cele doua distante sa se compare in acelasi spatiu.
CreateFramebuffer()
pentru a genera un nou obiect de tip framebuffer, impreuna cu texturile atasate la el.