Laboratorul 02

Framework-ul de laborator are o proiectare modulară, unde fiecare clasă specifică unui laborator poate fi executată individual. Pentru a specifica ce laborator să fie executat, trebuie să modificați fișierul main.cpp, la linia 49 și să initializați clasa specifică laboratorului dorit. De exemplu, pentru inițializarea laboratorului 2, codul arată în felul următor:

World *world = new lab::Lab2();

Banda grafică

Banda grafică reprezintă secvența de pași ce sunt realizați pentru crearea imaginii unui cadru.

Conceptul de bandă vine din contextul unei benzi de asamblare pentru diferite industrii.

Similar conceptului de bandă de asamblare a unei mașini, schițat în imaginea de mai jos, în care de-a lungul mai multor pași, la final rezultă o mașină aproape de forma ei finală, banda grafică reprezintă sunccesiunea de pași care produce la final imaginea unui cadru. Procesul de desenare se încheie la finalul benzii, dar asemănător cu procesul de creare a unei mașini, care mai necesită o serie de pași ulteriori, aplicația mai realizează câteva procese conexe pentru încheierea cadrului.

De altfel, traducerea în limba engleză a benzii grafice este graphics pipeline, ceea ce este similar cu traducerea în limba engleză pentru banda sau linia de asamblare, respectiv assembly line.

In continuarea acestui laborator, vom studia doar unul dintre pașii benzii grafice, ce poartă numele de proces de rasterizare.

Rasterizatorul

Banda grafică, implementată hardware în procesorul grafic și software în driver-ul companion tipului de procesor, poate fi utilizată în desenarea pe ecran a unor suprafețe complexe, în situația în care acestea sunt trimise la intrare în bandă sub forma unei rețele de triunghiuri. Un astfel de exemplu, poate fi văzut în imaginea de mai jos, unde trăsăturile feței sunt aproximate din unirea mai multor triunghiuri.

Noi vom studia, în următoarele 3 laboratoare de la această materie, procesul de desenare pe ecran a unei topologii descrise sub forma unei rețele de triunghiuri. Acest proces cuprinde mai mulți pași.

În acest laborator, ne vom concentra doar pe un singur pas, de bază, din tot procesul de desenare, mai exact, pasul de desenare în grila de pixeli a ecranului a unor triunghiuri în spațiul 2D. Procesul acesta, în banda grafică, poartă numele de rasterizare.

În imaginea de mai jos se poate observa o grilă de pixeli peste care este suprapus un triunghi, marcat in culoarea negru. În culoare albastră a fost marcat centrul fiecărui pixel. Stabilirea pixelilor ce se regasesc în interiorul triunghiului, marcați în culoarea verde, se realizează după regula: dacă centrul unui pixel se află în interiorul triunghiului sau pe latura lui, se consideră ca tot pixelul se află în interiorul triunghiului.

Interpolarea informației

Culoarea unui pixel ce se stabilește că se află în interiorul unui triunghi este dată de culoarea triunghiului. În banda grafică, pentru a oferi control utilizatorului, culoarea se precizează la nivel de vârf. Astfel, fiecare din cele 3 vârfuri ale unui triunghi conține informație de culoare.

În laboratorul 8 vom studia o abordare cu care se poate preciza informația la nivel de pixel.

În situația în care toate cele 3 vârfuri ale unui triunghi au aceeași culoare, toți pixelii ce se regăsesc în interiorul triunghiului au aceeași culoare. În situatia în care vârfurile au culori diferite, culoarea unui pixel se calculează prin interpolare, pe baza distanței față de fiecare vârf. Un exemplu vizual se poate găsi în imaginea de mai jos, unde vârful de sus are culoarea albastră, vârful din stânga jos, culoarea roșie și vârful din dreapta jos culoarea verde. Se poate vedea că, un pixel cu cât este mai apropiat de un anumit vârf, cu atât are o nuanță de culoare mai apropiată de cea a vârfului respectiv.

Pe lângă informația de culoare, fiecare vârf poate avea asociate și alte date. Această abordare poate fi folosită pentru orice tip de informație definită la nivel de vârfuri. Puțin mai jos vom vedea o abordare pentru a obține și noi acest rezultat.

Testul de adâncime

Vom propune, suplimentar, ca pe lângă coordonate 2D (x, y), fiecare vârf al unui triunghi să aibă și o coordonată de adâncime (z). Această coordonată nu este folosită pentru a stabili dacă un pixel se regăsește în interiorul unui triunghi, dar este folosită pentru rezolvarea problemei în care suprafețele a două triunghiuri se suprapun între ele.

Pentru a rezolva această situație, trebuie să avem o ordine de afișare. Presupunem că o valoare de adâncime (z) mai mică determină afișarea peste o valoare de adâncime mai mare. Banda grafică hotărăște dacă 2 triunghiuri se pot rasteriza concomitent. Pentru simplitate vom considera că se desenează secvențial exact 2 triunghiuri ce se suprapun.

  1. Primul triunghi este rasterizat în totalitate. Pentru fiecare pixel ce se află în interiorul lui, se calculează coordonata de adâncime, prin interpolare între, similar cu abordarea de mai sus, utilizată pentru calcularea culorii. Această coordonată se pastrează într-o grilă separată față de cea utilizata pentru a păstra pixelii.
  2. În momentul în care se rasterizează triunghiul 2, și se stabilește un pixel ce se află în interiorul acestui triunghi, se calculează coordonata de adâncime pentru pixel și se compară cu valoarea ce se află la aceeași locație în grila de valori de adâncime.
    1. În situația în care valoarea de adâncime a pixelului desenat acum este mai mică decât cea din grilă, se salvează în grila de pixeli și de adâncime, valorile pixelului curent.
    2. În situația în care valoarea de adâncime a pixelului desenat acum este mai mare decât cea din grilă, se consideră că pixelul ce trebuie să fie vizibil este deja în grilă și pixelul ce se procesează trebuie să fie în spatele lui, astfel că se renunță la pixelul curent.

Laborator

Descrierea rețelei de triunghiuri

În framework-ul de laborator, rețeaua de triunghiuri se va descrie prin două structuri de date. Prima este mulțimea ordonată de vârfuri:

vector<VertexFormat> vertices
{
    VertexFormat(glm::vec3(0, 50,  0.2f), glm::vec3(1, 0, 0)),
    VertexFormat(glm::vec3(70, 99,  0.2f), glm::vec3(0, 1, 0)),
    VertexFormat(glm::vec3(99, 0,  0.2f), glm::vec3(0, 0, 1)),
    VertexFormat(glm::vec3(10, 10,  0.2f), glm::vec3(0, 1, 1))
}

Structura de date VertexFormat conține mai multe informații pentru fiecare vârf. În constructorul ei, primii 2 parametri transmiși sunt poziția (x, y, z), cu (x, y) în grila de pixeli și coordonata (z) cu valori între 0 și 1, respectiv al doilea parametru este culoarea în modelul de culoare rgb, unde fiecare canal are valorile între 0 și 1. Astfel, culoarea roșie este definită ca (1, 0, 0), culoarea verde (0, 1, 0), culoarea cyan (0, 1, 1).

A doua structură de date utilizată pentru descrierea rețelei de triunghiuri este o mulțime ordonată de indici ai vârfurilor din prima structură de date:

vector<unsigned int> indices
{
    0, 1, 2,
    0, 2, 3
}

Fiecare triplet de indici consecutivi din această mulțime descrie vârfurile care formează un triunghi. De exemplu, tripletul 0, 1, 2 descrie triunghiul format din primele 3 vârfuri din vertices, iar tripletul 0, 2, 3 descrie triunghiul format de primul vârf și ultimele două.

Procesul de rasterizare

Procesul de rasterizare este implementat de cele mai multe ori hardware în procesorul grafic, dar pentru anumite procesoare, este implementat software în driver-ul companion al tipului de procesor grafic. Aceste procesoare au arhitecturi closed-source, astfel că metodele utilizate nu sunt publice. Din acest motiv, în acest laborator, veți implementa o versiune didactică a rasterizatorului, ce obține rezultate similare cu cel utilizat de procesorul grafic :) .

Abordarea propusă pentru implementare, în acest laborator, este următoarea:

  • Se parcurg pe rând toți pixelii din dreptunghiul încadrator al triunghiului
    • În situația în care centrul pixelului se află în interiorul triunghiului
      • Se calculează informația de adâncime a pixelului prin interpolare între vârfuri
      • În situația în care testul de adâncime confirmă că acest pixel trebuie să fie vizibil
        • Se calculează culoarea pixelului prin interpolare între vârfuri

Centrul unui pixel p, ce se află pe linia r și coloana c în grilă, se consideră că are coordonatele la poziția (c + 0.5, r + 0.5). Numeroatarea liniilor și coloanelor începe de la 0. De exemplu, pentru pixelul aflat pe linia 0 și coloana 0 în grila de pixeli, coordonatele centrului sunt (0.5, 0.5), iar centrul pixelului de pe linia 100 și coloana 150 este la coordonatele (150.5, 100.5).

Din acest motiv, o grilă de pixeli de rezoluție 1280×720, are ultima coloană egală cu 1279 și ultima linie egală cu 719. Observăm că rezoluția unei grile este dată mai întâi de lățime, ce reprezintă numărul de coloane și apoi de înălțime, ce reprezintă numărul de linii.

Interpolarea informației din vârfuri

Pentru a calcula informația unui pixel prin interpolare între vârfuri, se pot folosi coordonatele baricentrice ale centrului pixelului. Aceste coordonate sunt:

$$ P'=(u,v,w) \\ u= \frac{A_{\Delta P V_1 V_2}}{A_{\Delta V_1 V_2 V_3}} \\ v=\frac{A_{\Delta P V_1 V_3}}{A_{\Delta V_1 V_2 V_3}} \\ w=\frac{A_{\Delta P V_2 V_3}}{A_{\Delta V_1 V_2 V_3}} \\ $$

Aceste coordonate sunt descrise prin raportul dintre ariile unuia dintre triunghiurile interioare din imaginea de mai jos și aria triunghiului mare.

Pentru calcularea culorii punctului se folosește:

$$ C_P= u \cdot C_{V_3} + v \cdot C_{V_2} + w \cdot C_{V_1} $$

Această abordare poate fi utilizată pentru a calcula prin interpolare între vârfuri orice tip de informație asociată vârfurilor. De exemplu, pentru a calcula valoarea de adâncime a unui pixel, descrisă mai sus, se folosește:

$$ Z_P= u \cdot Z_{V_3} + v \cdot Z_{V_2} + w \cdot Z_{V_1} $$

Cerințe laborator

Completați clasa TriangleRasterizer cu următoarele:

  1. 0.15p - Completați metodele CheckPointInsideTriangle() și ComputeTriangleArea() pentru a calcula dacă un punct se află în interiorul unui triunghi, conform indicațiilor de mai sus.
  2. 0.1p - Completați metoda ComputePixelColor() pentru a calcula informația de culoare prin interpolare între vârfuri. Utilizați coordonatele baricentrice, descrise mai sus.
  3. 0.05p - Completați metoda ComputePixelDepth() pentru a calcula informația de adâncime prin interpolare între vârfuri. Utilizați coordonatele baricentrice, descrise mai sus.

Rezultatul final pe care ar trebui să îl obțineti este următorul:

Se poate observa că fiecare din cele două triunghiuri se află parțial în spate și parțial în fața celuilalt. Acest rezultat este obținut deoarece triunghiul ce are în vârfuri culorile roșu, verde și albastru are toate coordonatele de adâncime egale cu 0.5, iar triunghiul ce are în vârfuri culorile galben, magenta și cyan, are coordonata de adâncime a vârfului de culoare cyan egală cu 0 și coordonatele de adâncime a celorlalte 2 vârfuri egale cu 1. Din acest motiv, apare efectul de întretăiere a celor două triunghiuri.

ppbg/laboratoare/02.txt · Last modified: 2024/10/16 22:07 by andrei.lambru
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