This shows you the differences between two versions of the page.
ppbg:laboratoare:02 [2023/10/19 02:31] andrei.lambru |
ppbg:laboratoare:02 [2024/10/16 22:07] (current) andrei.lambru |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Laboratorul 02 ====== | ====== Laboratorul 02 ====== | ||
- | <note tip> | + | <note important> |
- | Pentru rezolvarea cerintelor din acest laborator, aveti nevoie de codul utilizat in rezolvarea cerintelor din cadrul laboratorului 1. In laboratoarele 2 si 3 vom reutiliza acest cod. In situatia in care nu ati rezolvat laboratorul 1, va trebui sa il realizati mai intai pe el si ulterior sa reveniti la cerintele celui curent. | + | 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: |
- | + | <code cpp> | |
- | **Reamintire!!** Puteti prezenta rezolvarile cerintelor de pana la 2 laboratoare, in fiecare saptamana. De exemplu, puteti prezenta laboratorul curent si pe cel din saptamana anterioara, in totalitate sau partial, inclusiv cerinta bonus. | + | World *world = new lab::Lab2(); |
+ | </code> | ||
</note> | </note> | ||
- | <note tip> | + | ===== Banda grafică ===== |
- | **Pentru studentii care au realizat laboratorul saptamana trecuta** | + | |
- | Va trebui sa [[https://github.com/UPB-Graphics/gfx-framework-ppbg | descarcati]] din nou framwork-ul de laborator, deoarece a suferit cateva modificari importante si sa copiati codul celor 4 metode: ''ComputeTriangleArea()'', ''CheckPointInsideTriangle()'', ''ComputePixelColor()'' si ''ComputePixelDepth()'', din versiunea de framework utilizata pentru rezolvarea cerintelor din laboratorul anterior, in versiunea curenta. | + | //**Banda grafică** reprezintă secvența de pași ce sunt realizați pentru crearea imaginii unui cadru.// |
- | Va fi necesar sa utilizati din nou utilitarul CMake. Pentru a va reaminti realizarea setup-ului, puteti sa consultati din nou [[:ppbg:setup-framework | pagina]] dedicata acestui lucru. | + | <note tip> |
- | </note> | + | Conceptul de //bandă// vine din contextul unei **benzi de asamblare** pentru diferite industrii. |
- | ===== Transformari in spatiul 2D ===== | + | 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. |
- | In laboratorul anterior, am vazut cum se poate desena un triunghi pe ecran, daca avem la dispozitie informatia necesara pentru fiecare varf: coordonatele in spatiul 2D, valoare de adancime si o culoare. Cu toate acestea, acest sistem nu este suficient de flexibil, deoarece, in cadrul unei aplicatii grafice in timp real, dorim sa realizam animatii cu un model 3D, reprezentat de o retea de triunghiuri. Coordonatele initiale ale triunghiurilor trebuie sa fie modificate pentru a realiza o animatie. Realizarea aceleeasi modificari, sau transformari, pentru toate varfurile unei retele de triunghiuri, produce, din punct de vedere vizual, efectul de aplicare a modificarii pentru intreg ansamblul, respectiv pentru tot modelul 3D. | + | 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//. |
- | Din acest motiv, de-alungul timpului, au fost standardizate mai multe tipuri de modificari asupra informatiei unui varf, in particular asupra coordonatelor lui, ce pot fi utilizate in vederea realizarea animatiilor. | + | {{ :ppbg:laboratoare:assembly-line.png?600 |}} |
- | ==== Transformarea de translatie (deplasare) ==== | + | </note> |
- | Transformarea cea mai de baza este cea utilizata pentru modificarea coordonatelor unui varf. Acest proces se realizeaza prin deplasarea coordonatelor unui varf cu un anumit vector de deplasare si poarta numele de translatie sau translatare. | + | In continuarea acestui laborator, vom studia doar unul dintre pașii benzii grafice, ce poartă numele de proces de **rasterizare**. |
- | In imaginea de mai jos, in partea dreapta, se poate observa rezultatul translatiei patratului din partea stanga cu vectorul de translatie $\vec{t}=\begin{bmatrix} | + | ===== Rasterizatorul ===== |
- | 2 & 1 | + | |
- | \end{bmatrix}$ . | + | |
- | {{ :ppbg:laboratoare:translation1.png?600 |}} | + | 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. |
- | + | ||
- | Formula pentru realizarea procesului de translatie este urmatoarea: | + | |
- | + | ||
- | $$ | + | |
- | x'=x+t_x\\ | + | |
- | y'=y+t_y | + | |
- | $$ | + | |
- | + | ||
- | ==== Transformarea de modificare a scarii ==== | + | |
- | + | ||
- | O alta transformare, ce poate fi utilizata in realizarea animatiilor, este modificarea scarii unui obiect, prin marirea sau micsorarea obiectului. | + | |
- | + | ||
- | In imaginea de mai jos, in partea dreapta, se poate observa rezultatul modificarii scarii pentru patratul din partea stanga, cu vectorul de scara $\vec{s}=\begin{bmatrix} | + | |
- | 2 & 2 | + | |
- | \end{bmatrix}$ . | + | |
- | + | ||
- | {{ :ppbg:laboratoare:scale1.png?600 |}} | + | |
- | + | ||
- | $$ | + | |
- | x'=s_x \cdot x\\ | + | |
- | y'=s_y \cdot y | + | |
- | $$ | + | |
- | + | ||
- | ==== Transformarea de rotatie ==== | + | |
- | + | ||
- | Ultima transformare des intalnita in grafica pe calculator este rotatia unui model 3D. Pentru realizarea acestui proces, vom utiliza un aparat matematic care se aplica in felul urmator: //rotatia unui triunghi se realizeaza prin rotatia tuturor varfurilor in sens trigonometric, fata de centrul axelor de coordonate //. | + | |
- | + | ||
- | Un exemplu pentru aceasta transformare se poate vedea in imaginea de mai jos, unde patratul este rotit cu 45 de grade, in sens trigonometric, fata de punctul v0, care este in centrul axelor de coordonate. | + | |
- | + | ||
- | {{ :ppbg:laboratoare:rotation1.png?600 |}} | + | |
- | + | ||
- | Formula pentru aceasta transformare este: | + | |
- | + | ||
- | $$ | + | |
- | x'=x \cdot cos(\theta)-y \cdot sin(\theta)\\ | + | |
- | y'=x \cdot sin(\theta)+y \cdot cos(\theta) | + | |
- | $$ | + | |
- | + | ||
- | <note> | + | |
- | Aceasta formula reiese din urmatorul fapt. Conform imaginii de mai jos, avem punctul V, pe care dorim sa il rotim cu unghiul B, in sens trigonometric, fata de originea axelor de coordonate pentru a obtine punctul V'. | + | |
- | + | ||
- | + | ||
- | {{ :ppbg:laboratoare:rotation2.png?200 |}} | + | |
- | + | ||
- | Componentele x si y ale coordonatei lui V se pot scrie si sub forma: | + | |
- | + | ||
- | + | ||
- | $$ | + | |
- | x=r \cdot cos(\alpha)\\ | + | |
- | y=r \cdot sin(\alpha) | + | |
- | $$ | + | |
- | + | ||
- | De asemenea, componentele x' si y' ale coordonatei lui V' se pot scrie sub forma: | + | |
- | + | ||
- | + | ||
- | + | ||
- | $$ | + | |
- | x'=r \cdot cos(\alpha + \beta)\\ | + | |
- | y'=r \cdot sin(\alpha + \beta)\\ | + | |
- | + | ||
- | $$ | + | |
- | + | ||
- | Continuam prin inlocuirea sumei din functiile trigonometrice cu: | + | |
- | + | ||
- | + | ||
- | + | ||
- | $$ | + | |
- | x'=r \cdot cos(\alpha) \cdot cos(\beta)-r \cdot sin(\alpha) \cdot sin(\beta)\\ | + | |
- | y'=r \cdot cos(\alpha) \cdot sin(\beta)+ r\cdot sin(\alpha) \cdot cos(\beta)\\ | + | |
- | + | ||
- | $$ | + | |
- | + | ||
- | In final, inlocuim cu x si y in rezultatul anterior, conform primei formule din aceasta nota si obtinem: | + | |
- | + | ||
- | + | ||
- | + | ||
- | $$ | + | |
- | x'=x \cdot cos(\theta)-y \cdot sin(\theta)\\ | + | |
- | y'=x \cdot sin(\theta)+y \cdot cos(\theta) | + | |
- | $$ | + | |
+ | {{ :ppbg:laboratoare:rasterizer-1.png?200 |}} | ||
+ | <note tip> | ||
+ | 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. | ||
+ | <hidden> | ||
+ | Secvența de pași utilizați în desenarea pe ecran a unei rețele de triunghiuri poartă numele de **bandă grafică** sau, cum este cunoscută în limba engleză, **rendering pipeline**. | ||
+ | </hidden> | ||
</note> | </note> | ||
+ | Î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**. | ||
- | ==== Compunerea transformarilor ==== | + | Î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//. |
- | Transformarile de baza, prezentate mai sus, nu sunt suficient de flexibile pentru a obtine orice tip de animatii prin utilizarea individuala a unui anume tip. Un exemplu este rotatia fata de \\propriul centru\\ al unui patrat de latura 1 ce a fost definit cu coltul stanga-jos in originea axelor de coordonate. O aplicare directa a transformarii de rotatie rezulta in rotatia patratului fata de coltul stanga-jos. Din acest motiv, este necesar sa obtinem rezultatul dorit prin compunerea mai multor transformari. Lantul de transformari este vizibil in imaginea de mai jos. | + | {{ :ppbg:laboratoare:rasterizer.png?300 |}} |
- | {{ :ppbg:laboratoare:rotation3.png?600 |}} | + | ==== Interpolarea informației ==== |
- | - Primul pas este translatarea patratului, astfel incat centrul lui sa se afle in originea axelor de coordonate | + | 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. |
- | - In acest moment, putem roti varfurile patratului fata de centrul lui | + | |
- | - Dupa rotatie, patratul ramane cu centrul in originea axelor de coordonate, astfel ca aplicam transformarea inversa de la punctul 1 pentru a readuce patratul cu centrul in pozitia lui originala | + | |
<note tip> | <note tip> | ||
- | Acelasi proces se poate aplica si pentru transformarea de modificare a scarii, pentru a face modificarea din centrul patratului :) . | + | În laboratorul 8 vom studia o abordare cu care se poate preciza informația la nivel de pixel. |
</note> | </note> | ||
- | ==== Forma matriceala a transformarilor ==== | + | Î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. |
- | Compunerea transformarilor definite mai sus pot avea o forma putin mai greoaie de calculat. De exemplu, formula pentru finala pentru compunerea de mai sus in care rotim un patrat fata de propriul centru, este: | + | {{ :ppbg:laboratoare:rasterizer-2.jpg?300 |}} |
+ | 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. |
- | x'=(x-0.5) \cdot cos(45)-(y-0.5) \cdot sin(45) + 0.5\\ | + | |
- | y'=(x-0.5) \cdot sin(45)+(y-0.5) \cdot cos(45) + 0.5 | + | |
- | $$ | + | |
- | === Vectori linie === | + | 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. |
+ | - 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. | ||
+ | - Î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. | ||
+ | - Î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. | ||
+ | - Î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. | ||
- | Pentru a simplifica procesul de calcul se foloseste forma matriceala a transformarilor. Pentru fiecare din cele 3 transformari de baza de mai sus, forma matriceala este: | + | ==== Laborator ==== |
- | $$ | ||
- | \begin{bmatrix} | ||
- | {x+t_x} & {y+t_y} & 1 | ||
- | \end{bmatrix} = | ||
- | \begin{bmatrix} | ||
- | x & y & 1 | ||
- | \end{bmatrix} | ||
- | \begin{bmatrix} | ||
- | 1 & 0 & 0\\ | ||
- | 0 & 1 & 0\\ | ||
- | t_x & t_y & 1 | ||
- | \end{bmatrix} | ||
+ | === 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: |
+ | <code cpp> | ||
+ | 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)) | ||
+ | } | ||
+ | </code> | ||
- | $$ | + | 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). |
- | \begin{bmatrix} | + | |
- | {s_x \cdot x} & {s_y \cdot y} & 1 | + | |
- | \end{bmatrix} = | + | |
- | \begin{bmatrix} | + | |
- | x & y & 1 | + | |
- | \end{bmatrix} | + | |
- | \begin{bmatrix} | + | |
- | s_x & 0 & 0\\ | + | |
- | 0 & s_y & 0\\ | + | |
- | 0 & 0 & 1 | + | |
- | \end{bmatrix} | + | |
+ | 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: | ||
- | $$ | + | <code cpp> |
+ | vector<unsigned int> indices | ||
+ | { | ||
+ | 0, 1, 2, | ||
+ | 0, 2, 3 | ||
+ | } | ||
+ | </code> | ||
+ | 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 === | ||
- | $$ | + | <note tip> |
- | \begin{bmatrix} | + | 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 :) . |
- | {x \cdot cos(\theta)-y \cdot sin(\theta)} & {x \cdot sin(\theta)+y \cdot cos(\theta)} & 1 | + | </note> |
- | \end{bmatrix} = | + | |
- | \begin{bmatrix} | + | |
- | x & y & 1 | + | |
- | \end{bmatrix} | + | |
- | \begin{bmatrix} | + | |
- | cos(\theta) & sin(\theta) & 0\\ | + | |
- | -sin(\theta) & cos(\theta) & 0\\ | + | |
- | 0 & 0 & 1 | + | |
- | \end{bmatrix} | + | |
+ | 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 | ||
- | Compunerea transformarilor se poate realiza prin inmultirea matricilor corespunzatoare fiecarei transformari. Astfel, pentru exemplul de mai sus, in care se roteste un patrat in jurul propriului centru, lantul de transformari este urmatorul: | + | <note tip> |
+ | 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 1280x720, 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. | |
- | + | ||
- | $$ | + | |
- | \begin{bmatrix} | + | |
- | x' & y' & 1 | + | |
- | \end{bmatrix} = | + | |
- | \begin{bmatrix} | + | |
- | x & y & 1 | + | |
- | \end{bmatrix} | + | |
- | \begin{bmatrix} | + | |
- | 1 & 0 & 0\\ | + | |
- | 0 & 1 & 0\\ | + | |
- | -0.5 & -0.5 & 1 | + | |
- | \end{bmatrix} | + | |
- | \begin{bmatrix} | + | |
- | cos(45) & sin(45) & 0\\ | + | |
- | -sin(45) & cos(45) & 0\\ | + | |
- | 0 & 0 & 1 | + | |
- | \end{bmatrix} | + | |
- | \begin{bmatrix} | + | |
- | 1 & 0 & 0\\ | + | |
- | 0 & 1 & 0\\ | + | |
- | 0.5 & 0.5 & 1 | + | |
- | \end{bmatrix} | + | |
- | + | ||
- | + | ||
- | $$ | + | |
- | + | ||
- | + | ||
- | <note> | + | |
- | Se poate observa ca doar transformarea de translatie necesita o matrice de dimensiune 3x3, iar pentru celelalte 2 transformari sunt necesare matrici de dimensiune 2x2. Sunt 2 motive principale pentru care se utilizeaza matrici de 3x3 pentru toate transformarile: | + | |
- | * Pentru a putea crea transformari compuse, operatia de inmultire intre matrici trebuie sa fie posibila, astfel ca se utilizeaza aceeasi dimensiune pentru toate tipurile de matrici de transformare | + | |
- | * In procesul de transformare perspectiva, ce va fi utilizata in laboratorul urmator, se utilizeaza coordonate intr-un spatiu omogen, ce necesita o informatie scalara suplimentara. Vom discuta mai multe detalii in legatura cu acest aspect laboratorul urmator :) . | + | |
</note> | </note> | ||
- | === Vectori coloana === | + | === Interpolarea informației din vârfuri === |
- | + | ||
- | Alternativ, se pot utiliza vectorii sub forma unei matrici coloana, de dimensiune 3x1. Pentru a utiliza astfel de vectori, toate matricile descrise anterior trebuie transpuse. Astfel, matricea de translatie devine: | + | |
+ | Pentru a calcula informația unui pixel prin interpolare între vârfuri, se pot folosi coordonatele baricentrice ale centrului pixelului. Aceste coordonate sunt: | ||
$$ | $$ | ||
- | \begin{bmatrix} | + | P'=(u,v,w) \\ |
- | x+t_x & y+t_y & 1 | + | u= \frac{A_{\Delta P V_1 V_2}}{A_{\Delta V_1 V_2 V_3}} \\ |
- | \end{bmatrix} ^t =( | + | 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}} \\ | |
- | \begin{bmatrix} | + | |
- | x& | + | |
- | y& | + | |
- | 1 | + | |
- | \end{bmatrix} \begin{bmatrix} | + | |
- | 1 & 0 & 0\\ | + | |
- | 0 & 1 & 0\\ | + | |
- | t_x & t_y & 1 | + | |
- | \end{bmatrix})^t | + | |
$$ | $$ | ||
+ | Aceste coordonate sunt descrise prin raportul dintre ariile unuia dintre triunghiurile interioare din imaginea de mai jos și aria triunghiului mare. | ||
- | Deoarece, conform urmatoarei proprietati a matricilor: | + | {{ :ppbg:laboratoare:rasterizer-4.png?200 |}} |
+ | Pentru calcularea culorii punctului se folosește: | ||
$$ | $$ | ||
- | (M_{0} \cdot M_{1} \cdot ... \cdot M_{n-1} \cdot M_{n})^{t} = M_{n}^{t} \cdot M_{n-1}^{t} \cdot ... \cdot M_{1}^{t}\cdot M_{0}^{t} | + | C_P= u \cdot C_{V_3} + v \cdot C_{V_2} + w \cdot C_{V_1} |
$$ | $$ | ||
- | Rezulta ca forma matricei pentru transformarea de translatie este: | + | 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: |
- | $$ | ||
- | \begin{bmatrix} | ||
- | x+t_x \\ y + t_y \\ 1 | ||
- | \end{bmatrix} = | ||
- | \begin{bmatrix} | ||
- | 1 & 0 & 0\\ | ||
- | 0 & 1 & 0\\ | ||
- | t_x & t_y & 1 | ||
- | \end{bmatrix}^t | ||
- | \begin{bmatrix} | ||
- | x& | ||
- | y& | ||
- | 1 | ||
- | \end{bmatrix} ^t= | ||
- | \begin{bmatrix} | ||
- | 1 & 0 & t_x\\ | ||
- | 0 & 1 & t_y\\ | ||
- | 0 & 0 & 1 | ||
- | \end{bmatrix} | ||
- | \begin{bmatrix} | ||
- | x\\ | ||
- | y\\ | ||
- | 1 | ||
- | \end{bmatrix} | ||
$$ | $$ | ||
- | + | Z_P= u \cdot Z_{V_3} + v \cdot Z_{V_2} + w \cdot Z_{V_1} | |
- | Analog pentru matricile celorlalte 2 transformari: | + | |
- | + | ||
$$ | $$ | ||
- | \begin{bmatrix} | ||
- | s_x \cdot x \\ s_y \cdot y \\ 1 | ||
- | \end{bmatrix} = | ||
- | \begin{bmatrix} | ||
- | s_x & 0 & 0\\ | ||
- | 0 & s_y & 0\\ | ||
- | 0 & 0 & 1 | ||
- | \end{bmatrix} | ||
- | \begin{bmatrix} | ||
- | x\\ | ||
- | y\\ | ||
- | 1 | ||
- | \end{bmatrix} | ||
- | $$ | ||
+ | ===== Cerințe laborator ===== | ||
- | $$ | + | Completați clasa ''TriangleRasterizer'' cu următoarele: |
- | \begin{bmatrix} | + | - 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. |
- | x \cdot cos(\theta)-y \cdot sin(\theta) \\ x \cdot sin(\theta)+y \cdot cos(\theta) \\ 1 | + | - 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. |
- | \end{bmatrix} = | + | - 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. |
- | \begin{bmatrix} | + | |
- | cos(\theta) & -sin(\theta) & 0\\ | + | |
- | sin(\theta) & cos(\theta) & 0\\ | + | |
- | 0 & 0 & 1 | + | |
- | \end{bmatrix} | + | |
- | \begin{bmatrix} | + | Rezultatul final pe care ar trebui să îl obțineti este următorul: |
- | x\\ | + | |
- | y\\ | + | |
- | 1 | + | |
- | \end{bmatrix} | + | |
- | $$ | + | |
- | Pentru transformarea compusa data exemplu mai devreme, vom avea: | + | {{ :ppbg:laboratoare:rasterizer-5.png?600 |}} |
- | + | ||
- | + | ||
- | + | ||
- | $$ | + | |
- | \begin{bmatrix} | + | |
- | x' \\ y' \\ 1 | + | |
- | \end{bmatrix} = | + | |
- | \begin{bmatrix} | + | |
- | 1 & 0 & 0.5\\ | + | |
- | 0 & 1 & 0.5\\ | + | |
- | 0 & 0 & 1 | + | |
- | \end{bmatrix} | + | |
- | \begin{bmatrix} | + | |
- | cos(45) & -sin(45) & 0\\ | + | |
- | sin(45) & cos(45) & 0\\ | + | |
- | 0 & 0 & 1 | + | |
- | \end{bmatrix} | + | |
- | \begin{bmatrix} | + | |
- | 1 & 0 & -0.5\\ | + | |
- | 0 & 1 & -0.5\\ | + | |
- | 0 & 0 & 1 | + | |
- | \end{bmatrix} | + | |
- | + | ||
- | \begin{bmatrix} | + | |
- | x \\ y \\ 1 | + | |
- | \end{bmatrix} | + | |
- | + | ||
- | $$ | + | |
<note tip> | <note tip> | ||
- | Observam ca, in situatia in care folosim vectori coloana, ordinea de aplicare a transformarilor este **de la ultima matrice inmultita la prima**. In plus, vectorul pentru care se aplica lantul de transformari este intotdeauna la final. | + | 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. |
</note> | </note> | ||
- | |||
- | === Libraria glm === | ||
- | |||
- | <note tip> | ||
- | In cadrul laboratoarelor de la aceasta materie, pana la finalul semestrului, vom utiliza vectori in forma coloana. Ambele forme, atat forma linie si forma coloana a vectorilor, sunt la fel de bune si compatibile cu procesorul grafic. | ||
- | </note> | ||
- | |||
- | Din motive de optimizare a accesului la memoria procesorului grafic, libraria glm foloseste la baza matrici column-major, mai precis, matrici care au informatia transpusa in memorie. Astfel, in constructorul tipului de date ''glm::mat3'', trebuie transmisa forma transpusa a matricei pe care dorim sa o retinem. | ||
- | |||
- | De exemplu, matricea pentru transformarea de translatie, utilizata pentru inmultirea cu **vectori coloana**, trebuie initializata astfel: | ||
- | |||
- | <code cpp> | ||
- | glm::mat3 Translate(float tx, float ty) | ||
- | { | ||
- | return glm::mat3( | ||
- | 1, 0, 0, // coloana 1 in memorie | ||
- | 0, 1, 0, // coloana 2 in memorie | ||
- | tx, ty, 1); // coloana 3 in memorie | ||
- | |||
- | } | ||
- | </code> | ||
- | |||
- | De asemenea, avem optiunea sa o initializam direct cu liniie si sa o transpunem ulterior: | ||
- | |||
- | <code cpp> | ||
- | glm::mat3 Translate(float tx, float ty) | ||
- | { | ||
- | return glm::transpose( | ||
- | glm::mat3( 1, 0, tx, | ||
- | 0, 1, ty, | ||
- | 0, 0, 1) | ||
- | ); | ||
- | } | ||
- | </code> | ||
- | |||
- | ==== Transformarea fereastra-poarta ==== | ||
- | |||
- | In situatia in care dorim ca aplicatia grafica proiectata de noi sa nu depinda de rezolutia ferestrei si astfel sa poata rula la rezolutii diferite, indiferent care ar fi acestea, trebuie sa nu mai definim coordonatele varfurilor in coordonatele pixelilor de pe ecran. Pentru acest proces, vom defini un spatiu special, denumit spatiu logic, in a carui coordonate vom defini | ||
- | |||
- | In laborator, a fost definit spatiul logic, dupa cum urmeaza: | ||
- | |||
- | <code cpp> | ||
- | struct ViewportSpace | ||
- | { | ||
- | int x; | ||
- | int y; | ||
- | int width; | ||
- | int height; | ||
- | }; | ||
- | |||
- | struct LogicSpace | ||
- | { | ||
- | float x; | ||
- | float y; | ||
- | float width; | ||
- | float height; | ||
- | }; | ||
- | </code> | ||
- | |||
- | De exemplu, pentru a transforma obiectele definite in spatiul logic ce are coltul stanga jos la coordonatele (1, 1) si rezolutia de 3x2, astfel incat sa fie puse pe tot ecranul, de rezolutie 1280x720, se pot defini spatiile in felul urmator. De asemenea, transformarea se poate vedea vizual in imaginea de mai jos. | ||
- | |||
- | <code cpp> | ||
- | ViewportSpace viewport_space = { 0, 0, 1280, 720 }; | ||
- | LogicSpace logic_space = { 1, 1, 3, 2 }; | ||
- | </code> | ||
- | |||
- | |||
- | {{ :ppbg:laboratoare:viewport.png?600 |}} | ||
- | |||
- | Lantul de transformari necesar acestui proces este: | ||
- | |||
- | |||
- | |||
- | $$ | ||
- | \begin{bmatrix} | ||
- | x' \\ y' \\ 1 | ||
- | \end{bmatrix} = | ||
- | \begin{bmatrix} | ||
- | 1 & 0 & V_{x}\\ | ||
- | 0 & 1 & V_{y}\\ | ||
- | 0 & 0 & 1 | ||
- | \end{bmatrix} | ||
- | \begin{bmatrix} | ||
- | V_{w}/L_{w} & 0 & 0\\ | ||
- | 0 & V_{h}/L_{h} & 0\\ | ||
- | 0 & 0 & 1 | ||
- | \end{bmatrix} | ||
- | \begin{bmatrix} | ||
- | 1 & 0 & -L_x\\ | ||
- | 0 & 1 & -L_y\\ | ||
- | 0 & 0 & 1 | ||
- | \end{bmatrix} | ||
- | |||
- | \begin{bmatrix} | ||
- | x \\ y \\ 1 | ||
- | \end{bmatrix} | ||
- | |||
- | $$ | ||
- | |||
<hidden> | <hidden> | ||
- | <note tip> | + | Bonus: |
- | Observati ca spatiul de vizualizare nu are tipuri de date cu virgula mobila, deoarece reprezinta o pozitie si o rezolutie in grila de pixeli a ferestrei. | + | - Generati geometria pentru un cerc format din mai multe triunghiuri |
- | </note> | + | |
</hidden> | </hidden> | ||
- | |||
- | <note tip> | ||
- | Utilizati tastele **sus**, **jos**, **stanga** si **dreapta** pentru a controla interactiv pozitia coltului din stanga-jos a spatiului logic. | ||
- | </note> | ||
- | |||
- | ==== Cerințe laborator ==== | ||
- | |||
- | - 0.1p - Completați fisierul ''transform2D.h'' cu cele 4 transformari descrise mai sus: | ||
- | - 0.05p - Aplicati transformarile necesare pentru a modifica linia de patrate din partea de sus, conform rezultatului din imaginea de mai jos. Consultati comentariile din cod pentru a primi mai multe detalii despre fiecare transformare. | ||
- | - 0.1p - Aplicati transformarile necesare pentru a modifica linia de patrate din partea de jos, conform rezultatului din imaginea de mai jos. Consultati comentariile din cod pentru a primi mai multe detalii despre fiecare transformare. \\ Pana in acest punct, rezultatul pe care ar trebui să îl obțineti este următorul: {{ :ppbg:laboratoare:transformations1.png?600 |}} | ||
- | - 0.05p - Impartiti ecranul cadranul in 4 cadrane si desenati formele geometrice de mai sus de 4 ori, cate o data in fiecare cadran. Modificati spatiul portii de vizualizare, pozitia coltului din stanga jos, respectiv, latimea si inaltimea. | ||
- | |||
- | Rezultatul final ar trebui sa fie urmatorul: | ||
- | |||
- | {{ :ppbg:laboratoare:transformations2.png?600 |}} | ||
- | |||
- | Bonus: Realizati razele unei stele prin desenarea multipla a unui triunghi rotit. Este necesar sa construiti o noua geometrie pentru triunghiul ce reprezinta o raza. Un exemplu vizual ar putea fi: | ||
- | |||
- | {{ :ppbg:laboratoare:transformations3.png?600 |}} | ||
- | |||
- | <note tip> | ||
- | * Aveti in vedere ca, pentru simplitate, triunghiul ce reprezinta raza, poate fi creat direct cu proprietatea de a fi isoscel :) . | ||
- | * Razele **se pot** suprapune intre ele. | ||
- | </note> | ||