This is an old revision of the document!
lab_list.h
, linia #include “lab/lab7/lab7.h”
.
Un obiect de tip textură reprezintă o secvență continuă de informație în interiorul memoriei RAM a procesorului grafic. În particular, în cadrul acestui laborator, ne referim la obiecte de tip textură ce pastrează informație din interiorul unei grile de două dimensiuni.
Într-o grilă de două dimensiuni, putem stoca informația unei imagini :) în care, în fiecare celulă, păstrăm informația unui pixel.
Pentru a vizualiza toate tipurile posibile, puteți consulta documentația oficială: https://www.khronos.org/opengl/wiki/Texture#Theory.
Conținutul informației unei imagini se poate proiecta pe suprafața unui model 3D, respectiv pe suprafața triunghiurilor din care este compusă rețeaua de triunghiuri ce descrie modelul 3D, prin utilizarea unei informații suplimentare, în fiecare vârf, denumită coordonată de textură. O astfel de coordonată are două dimensiuni și fiecare componentă a ei, respectiv x și y, se află în intervalul [0,1]. Aceasta este coordonata în spațiul finit al grilei 2D stocate în obiectul de tip textură, spațiu delimitat în colțul din stânga-jos de coordonata (0, 0) și în colțul din dreapta-sus de coordonata (1, 1). Un astfel de spațiu poate fi utilizat pentru a eșantiona valorile din grilă.
Pentru a exemplifica, în partea stângă a imaginii de jos se poate observa un triunghi pentru care s-au asociat în vârfuri coordonate de textură: (0.7, 0.75), (0.1, 0.3) și (0.95, 0.2). Presupunem că prin procesul de rasterizare, s-a obținut un pixel ce se află în interiorul acestui triunghi și care a obținut prin interpolare de la vârfuri coordonata de textură (0.7, 0.55). Prin transformarea acestei coordonate în spațiul grilei 2D, obținem valoarea (716.8, 563.2).
Avem mai multe posibilități pentru a eșantiona o valoare din informația celulelor din grila 2D.
Se eșantioneaza direct valoarea celulei de la coordonata în spațiul grilei 2D, cu valoarea fiecărei componente a coordonatei rotunjită în jos. Astfel, pentru exemplul de sus, unde coordonata de texturare este (716.8, 563.2), se eșantionează valoarea celulei (716, 563).
O altă variantă utilizează o eșantionare a informației prin interpolare liniară de la toate celulele din jurul coordonatei, respectiv:
glm::vec3 c1 = glm::vec3(716, 563); glm::vec3 c2 = glm::vec3(716, 564); glm::vec3 c3 = glm::vec3(717, 563); glm::vec3 c4 = glm::vec3(717, 564); glm::vec3 c12 = glm::mix(c1, c2, 0.8); // din partea fractionara a componentei x, 716.8 glm::vec3 c34 = glm::mix(c3, c4, 0.8); // din partea fractionara a componentei x, 716.8 glm::vec3 c1234 = glm::mix(c12, c34, 0.2) // din partea fractionara a componentei y, 563.2
$$ mix(a, b, t) = (1-t) \cdot a + t \cdot b $$
Conceptul de piramidă “mipmap” a fost propus de Lance Williams în anul 1983 și constă în stocarea unei “piramide” de versiuni de diferite rezoluții ale unei imagini. Un astfel de exemplu se poate observa în imaginea de mai jos, în care în aceeași grilă, în partea stângă este stocată imaginea la rezoluția inițială, iar în partea dreaptă sunt stocate versiunile imaginii inițiale la rezoluție de 2, 4, 8, până la 128 de ori mai mică.
Această piramidă este utilă în momentul în care este dorită o eșantionare într-o vecinatate mai mare de 2×2. Situația aceasta apare în momentul în care prin procesul de interpolare, doi pixeli vecini obținuti prin rasterizare au atribuite coordonate de texturare ce in grila 2D se află la distanță mai mare de o celulă. De exemplu, dacă doi pixeli vecini se află la distanță de 50 de celule în grila 2D, putem considera că dimensiunea pixelului, în spațiul grilei 2D este de 50 de celule. Astfel, putem utiliza versiunile imaginii ce au rezoluția cea mai apropiată de dimensiunea pixelului. Pentru imaginea de mai sus, aceste versiuni au rezoluție de 64×64 și 32×32. Pentru eșantionare, avem două opțiuni.
Se eșantionează direct sau liniar DOAR versiunea imaginii ce are rezoluția cea mai apropiată. Pentru exemplul de mai sus, se alege versiunea imaginii de rezoluție 64×64.
Se eșantionează direct sau liniar cele două versiuni ale imaginii ce au rezoluția cea mai apropiată și se interpolează liniar între cele două valori obținute din eșantionarea celor două versiuni.
Pentru gestionarea unui obiect de tip textura in API-ul grafic OpenGL, trebuie creat un astfel de obiect:
unsigned int texture_id; glGenTextures(1, &texture_id);
Pentru a modifica informatiile stocate de obiectul de tip textura sau pentru a utiliza obiectul, el trebuie legat:
glBindTexture(GL_TEXTURE_2D, texture_id);
Pentru a asocia informatie obiectului de tip textura, se utilizeaza:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
Suplimentar, se pot specifica parametri pentru diferite procese in care este implicat obiectul de tip textura.
Pentru procesul de esantionare, se pot specifica parametri:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Pentru parametrul de filtrare in situatia in care doi pixeli vecini obtinuti prin rasterizare au coordonate de textura la distanta mai mare de o celula din grila 2D, prin parametrul GL_TEXTURE_MIN_FILTER
se poate specifica:
GL_NEAREST
- esantionare directa doar din imaginea initialaGL_LINEAR
- esantionare liniara doar din imaginea initialaGL_NEAREST_MIPMAP_NEAREST
: esantionare directa din versiunea imaginii initiale ce are rezolutia cea mai apropiata de dimensiunea pixelului in spatiul grilei 2DGL_LINEAR_MIPMAP_NEAREST
: esantionare liniara din versiunea imaginii initiale ce are rezolutia cea mai apropiata de dimensiunea pixelului in spatiul grilei 2DGL_NEAREST_MIPMAP_LINEAR
: interpolare liniara intre valorile obtinute prin esantionare directa a versiunilor imaginii initiale ce au rezolutiile cele mai apropiate de dimensiunea pixelului in spatiul grilei 2DGL_LINEAR_MIPMAP_LINEAR
: interpolare liniara intre valorile obtinute prin esantionare liniara a versiunilor imaginii initiale ce au rezolutiile cele mai apropiate de dimensiunea pixelului in spatiul grilei 2D
Pentru parametrul de filtrare in situatia in care doi pixeli vecini obtinuti prin rasterizare au coordonate de textura la distanta mai mica de o celula din grila 2D, prin parametrul GL_TEXTURE_MAG_FILTER
se poate specifica:
GL_NEAREST
- esantionare directa doar din imaginea initialaGL_LINEAR
- esantionare liniara doar din imaginea initialaPentru mai multe detalii in legatura cu parametrii ce pot fi specificati, puteti consulta documentatia oficiala: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexParameter.xhtml.
Pentru a genera piramida “mipmap” se utilizeaza:
glGenerateMipmap(GL_TEXTURE_2D);
Procesul de esantionare este realizat de o entitate autonoma implementata hardware sub forma unui procesor in interiorul procesorului grafic sau software in driver-ul companion procesorului. Aceasta entitate autonoma este numita unitate de texturare si numarul lor este in general limitat la 32.
Pentru a utiliza procesul de esantionare, trebuie sa asociem obiectul de tip textura pe care dorim sa il esantionam cu o unitate de texturare, numerotat cu identificatori de la 0 la 31 si sa transmitem la un program de tip shader numarul unitatii de texturare:
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture_id); glUniform1i(glGetUniformLocation(shader->program, "texture_unit"), 0);
Pentru a utiliza unitatea de texturare, putem folosi intr-un program de tip shader texture()
:
#version 330 uniform sampler2D texture_unit; in vec2 texture_coord; layout(location = 0) out vec4 out_color; void main() { vec4 color = texture(texture_unit, texture_coord); out_color = color; }
In interiorul memoriei stocate de un obiect de tip textura se pot pastra informatii generice.
De exemplu, intr-o grila 2D se poata pastra informatia de culoare in componentele rgb si in componenta alpha o informatie suplimentara, binara, ce reprezinta faptul ca un pixel ce esantioneaza valoarea 1, ramane desenat iar la un pixel ce esantioneaza valoare 0 se renunta. Un astfel de exemplu este in imaginea de mai jos, unde in partea stanga se prezinta doar valoarea componentei alpha.
Pentru a renunta la desenarea unui pixel ce a esantionat valoarea 0 se poate folosi directiva discard
:
vec4 color = texture2D(texture_unit, texture_coord); if(color.a < 0.5) { discard; }
Directiva discard
opreste programul de tip fragment shader la apelul ei si nu se salveaza nicio valoare din atributele de iesire ale programului.
Un alt exemplu de aplicare a utilizarii obitectelor de tip textura pentru a stoca informatii generice este utilizarea grilei 2D pentru a pastra informatie de inaltime. Astfel, la desenarea unui plan cu o densitate mare de triunghiuri, se poate modifica componenta y a coordonatei fiecarui varf, pe baza informatiei din grila 2D, proiectata pe toata suprafata planului. Un exemplu de imagine ce pastreaza informatie de inaltime se poate vedea mai jos:
Rezultatul deformarii unui plan pe baza imaginii de mai sus este:
CreateTexture()
pentru a crea un obiect de tip textură.RenderSimpleMesh()
pentru a asocia o unitate de texturare fiecărei din cele 2 obiecte de tip textură și pentru a transmite la programul de tip shader numerele unităților de texturare asociate.LabShader.VS.glsl
calcularea valorii atributului de iesire pentru coordonata de textura.LabShader.FS.glsl
esantionarea informatiei dintr-un singur obiect de tip textura.discard
pentru a opri programele de tip fragment shader pe baza valorii componentei alpha esantionate.Init()
, specificati coordonatele de textura ale varfurilor unui patrulater. Aveti in vedere ca directiva glTexImage2D()
considera ca prima linie de pixeli a unei imagini incepe din partea din stanga-jos, astfel ca este necesara inversarea componentei y a coordonatelor de textura.CreateStrippedTexture()
pentru a crea continutul unei grile 2D ce contine o culoare RGB in fiecare celula. Grila 3D trebuie sa contina acelasi triplet (R, G, B) in toate celulele ce se afla pe aceeasi linie. Altfel spus, toate valorile de culoare de pe o linie sunt identice. Culoarea celulelor unei linii este aleasa aleator. Un exemplu de rezultat posibil este:vec3 color = mix(color1, color2, 0.5f);
Engine::GetElapsedTime()
.
Bonus: Utilizati imaginile lab7/images/snow.png
si lab7/images/water.png
, impreuna cu valoarea de inaltime a fiecarui pixel, pentru a obtine rezultatul de mai jos. Observati ca trecerea de la zona de apa la cea de pamant este graduala. Acelasi lucru este valabil si pentru trecerea de la zona de pamant la cea de zapada :) .