Laboratorul 06

Maparea mediului inconjurator

Introducere

Maparea mediului inconjurator reprezinta o metoda prin care putem interoga informatia din jurului unui obiect. Fie ca facem acest lucru pentru reflexii, refractii sau alte efecte mai complicate ideea este aceeasi: de a putea reprezenta intr-un mod eficient mediul inconjurator.

In imaginea de mai sus se poate observa cum o raza ce pleaca din camera se poate descompune in doua traiectorii distincte: una reflectata si una refractata. In realitate procesul este mai complicat dar pentru o iluzie vizuala acest model este suficient. Problema interesanta ce este usor de observat in acest desen este problema intersectiilor. Rasterizarea este un proces de intersectie coerent, bazat pe faptul ca intersectia intre primitiva si raster are loc intr-un mod cu localitate in mare masura garantata a fi buna. Problema cu un model bazat pe raze este ca nu mai avem mecanismul de rasterizare pentru a calcula foarte eficient intersectiile. Alternativele sunt raze, ca la ray-tracing, ce duc la un proces foarte costisitor si incoerent din punct de vedere al localitatii spatiale sau aproximari. Una din aceste aproximari se numeste cube mapping.

Ce este un cubemap

Un cubemap este un tip special de textura care este formata din 6 imagini: pozitiv x, negativ x, pozitiv y, negativ y, pozitiv z, negativ z. Aceste 6 imagini reprezinta o impartire a unui cub in 6 planuri, asa cum poate fi observat in imaginea urmatoare. Un cubemap reprezinta de obicei culoarea de la mediul inconjurator, pe toate directiile. Deci, un cubemap poate fi perceput ca un set de elemente de tip (directie, culoare). Un cubemap poate fi citit doar cu coordonate de texturare tridimensionale, pentru ca interogam culoarea din harta pentru directia (coordonata de texturare) introdusa. Mai mult cum coordonatele de texturare sunt de fapt directii, ele pot fi si negative.

Formatul in care veti gasi de obicei acest tip de textura este ca 6 imagini diferite sau ca o imagine cu urmatoarea forma.

Un cubemap este creat la fel ca orice alta textura, cu comanda:

glGenTextures(1, &gl_texture_object);

Cubemap-ul este legat la banda grafica la un nou punct de legare per unitatea de texturare curent legata, numit GL_TEXTURE_CUBE_MAP:

glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture_object);

Din punct de vedere al filtrarii procesul este identic cu cel al unei texturi bidimensionale, doar ca se poate folosi si:

glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);

care imbunatateste efectele vizuale la filtrarea de la marginea dintre fete. Pentru a introduce date in aceasta textura, se introduc pe rand toate cele 6 imagini, fiecare pe o pozitie specifica:

glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data_posx);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data_posy);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data_posz);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data_negx);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data_negy);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data_negz);

Shadere

Pentru a fi utilizat in shader, un cubemap trebuie legat la banda grafica, proces identic cu cel pentru o textura 2D normala. In schimb, in shader se foloseste un sampler diferit, numit samplerCube care functioneaza doar cu coordonate de texturare tridimensionale.

Pentru a implementa reflexia si refractia se pot folosi functiile de glsl:

reflect( RazaIncidenta, Normala) 
refract( RazaIncidenta, Normala, indexMediuCurent/indexMediuNou)

ce ne ofera directia razei reflectate sau refractate. Dar cum cubemap-ul reprezinta de fapt o harta de culoare interogata prin directie, putem folosi razele reflectate/refractate din fragment ca si coordonate de texturare pentru a afla culoarea mediului din directia razelor.

Rezultatul final pentru o reflexie poate fi calculat cu:

vec3 culoarefragment = texture(cubemap, reflect(RazaIncidenta, Normala));

Observatii:

  • pentru refractia unei raze din aer (index de refractie 1.0) in apa (index de refractie 1.33) apelul de functie ar fi refract(I, N, 1.0/1.33);
  • raza incidenta vine de la camera la suprafata, nu invers!

Rezultate (reflexie, apoi refractie)

Crearea unui framebuffer

Crearea si utilizarea unui framebuffer a mai fost detaliata si in laboratorul 3. Astfel, initializarea si legarea framebuffer-ului se va face similar.

Particularitatea laboratorului curent este ca acest framebuffer va trebui sa contina o textura de culoare de tip cubemap.

Acest lucru se face prin legarea unei texturi utilizand tipul GL_TEXTURE_CUBE_MAP.

glGenTextures(1, &color_texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, color_texture);

Lucrand cu o textura de tip cubemap, aceasta trebuie initializata pe toate cele 6 fete. Pentru acest lucru avem de asemenea la dispozitie tipuri dedicate pentru acestea:

  • GL_TEXTURE_CUBE_MAP_POSITIVE_X
  • GL_TEXTURE_CUBE_MAP_NEGATIVE_X
  • GL_TEXTURE_CUBE_MAP_POSITIVE_Y
  • GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
  • GL_TEXTURE_CUBE_MAP_POSITIVE_Z
  • GL_TEXTURE_CUBE_MAP_NEGATIVE_Z

Pentru a asocia texturi cu format de culoare pentru cele 6 fete, putem utiliza:

glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
...

Valoarea lor este incrementala, astfel ca se poate folosi si iterativ prin GL_TEXTURE_CUBE_MAP_POSITIVE_X+i.

Pentru a asocia texturi cu format de adancime, putem utiliza:

glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
...

Cubemap dinamic

Pentru a crea un cubemap dinamic, la fiecare frame trebuie sa pozitionam camera pe obiectul pe care vrem sa aplicam cubemap-ul si sa facem capturi pentru toate cele 6 directii ale cubemap-ului.

Acest lucru se poate face prin mai multe modalitati:

  • multi-pass: desenarea scenei de mai multe ori, din fiecare directie a camerei, specificand si care textura din cubemap (mai exact care fata a cubului) trebuie colorata
  • single-pass, prin trimiterea tuturor matricilor de vizualizare (practic care contin pozitia si orientarea camerei) si duplicarea geometriei in Geometry Shader

In acest laborator vom aborda tehnica single pass. Astfel, in Geometry Shader, geometria trebuie redata de 6 ori, cate o data pentru fiecare directie a camerei.

Atentie! In momentul in care multiplicati geometria trebuie sa specificati o valoarea suficient de mare pentru numarul maxim de vertecsi de output in GS.

Matricile de vizualizare se pot trimite separat, intr-o structura sau intr-un array. Exemplu:

glm::mat4 cubeView[6] =
  { 
     glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f,-1.0f, 0.0f)), // +X
     glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f,-1.0f, 0.0f)), // -X
     glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)), // +Y
     glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f,-1.0f, 0.0f), glm::vec3(0.0f, 0.0f,-1.0f)), // -Y
     glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f,-1.0f, 0.0f)), // +Z
     glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f,-1.0f), glm::vec3(0.0f,-1.0f, 0.0f)), // -Z
 };
 
glUniformMatrix4fv(glGetUniformLocation(shader->GetProgramID(), "viewMatrices"), 6, GL_FALSE, glm::value_ptr(cubeView[0]));

Layered rendering

Redarea stratificată este procesul prin care GS trimite primitive specifice la diferite straturi ale unui framebuffer. Acest lucru poate fi util pentru realizarea umbrelor bazate pe un cubmap sau pentru maparea mediului inconjurator folosind un cubemap, fără a fi nevoie să redați întreaga scenă de mai multe ori.

Redarea stratificată în GS funcționează prin două variabile speciale de ieșire:

out int gl_Layer;
out int gl_ViewportIndex; // Necesită GL 4.1 sau ARB_viewport_array.

Ieșirea gl_Layer definește la ce strat din imaginea stratificată merge geometria. Fiecare vârf din primitivă trebuie să obțină același indice de strat.

Atunci când se foloseste într-un cubemap, valoarea gl_Layer reprezintă fețele cubului.

gl_ViewportIndex, care necesită GL 4.1 sau ARB_viewport_array, specifică ce index de vizualizare să fie utilizat cu această primitivă, foarte util de exemplu pentru redarea in VR pentru cele 2 ecrane.

Astfel, putem folosi variabila gl_Layer pentru a specifica practic fata cubului dintr-un cubemap pe care se va reda, putand itera prin fiecare fata pentru redare stratificată. Cele 6 fețe au ordinea deja mentionata:

Layer number Cubemap face
0 GL_TEXTURE_CUBE_MAP_POSITIVE_X
1 GL_TEXTURE_CUBE_MAP_NEGATIVE_X
2 GL_TEXTURE_CUBE_MAP_POSITIVE_Y
3 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
4 GL_TEXTURE_CUBE_MAP_POSITIVE_Z
5 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
for (layer = 0; layer < 6; layer++) {
        gl_Layer = layer;
 
        //redarea primitivei din directia camerei specifica layer-ului curent, 
        //folosind setul de matrici de vizualizare primite in variabila viewMatrices
        ...
 
}

Variabila gl_Layer se sterge dupa folosirea comenzii EndPrimitive() astfel ca trebuie completata pentru fiecare primitiva in parte.

Rezultat final

Cerinte laborator

  1. Incarcati cubemap-ul in memoria procesorului grafic
  2. Implementati reflexia in fisierul CubeMap.FS.glsl
  3. Implementati refractia in fisierul CubeMap.FS.glsl (se pot folosi tastele 1 si 2 pentru switch intre reflexie si refractie)
  4. In scena este un obiect care se invarte incontinuu. Afisati reflexia acestuia pe mesh-ul central. Pentru acest lucru trebuie sa:
    1. realizati un framebuffer initializat cu doua texturi de tip cubemap, una cu format de culoare si cealalta cu format de adancime
    2. actualizati cubemap-ul la fiecare frame prin redarea single-pass a scenei cu cele 6 directii de camera in fisierul Framebuffer.GS.glsl
    3. specificati o valoare suficient de mare pentru numarul maxim de vertecsi de output in GS
    4. verificati aplicarea texturii actualizate pe mesh
spg/laboratoare/06.txt · Last modified: 2023/11/10 08:13 by alexandru.gradinaru
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