Differences

This shows you the differences between two versions of the page.

Link to this comparison view

ppbg:laboratoare:04 [2023/11/01 21:30]
andrei.lambru
ppbg:laboratoare:04 [2024/11/01 00:22] (current)
andrei.lambru
Line 1: Line 1:
 ====== Laboratorul 04 ====== ====== Laboratorul 04 ======
  
-<​note ​tip+<​note ​important
-Pentru rezolvarea cerințelor din acest laborator, aveți nevoie de codul utilizat în rezolvarea cerințelor din cadrul laboratorului 3. În situatia în care nu ați rezolvat ​acest [[:​ppbg:​laboratoare:​03|laboratorul 3]], va trebui să le realizați mai întâi pe el și ulterior să reveniți la cerințele celui curent+Pentru rezolvarea cerințelor din acest laborator, aveți nevoie de codul utilizat în rezolvarea cerințelor din cadrul ​laboratorului 2 și al laboratorului 3. În situatia în care nu ați rezolvat ​[[:​ppbg:​laboratoare:​02|laboratorul 2]] sau [[:​ppbg:​laboratoare:​03|laboratorul 3]], va trebui să le realizați mai întâi pe ele și ulterior să reveniți la cerințele celui curent.
- +
-**Reamintire!!!** Puteți prezenta rezolvările cerințelor de până la 2 laboratoare,​ în fiecare săptămână. De exemplu, puteți prezenta laboratorul curent și pe cel din săptămâna anterioară,​ în totalitate sau parțial, inclusiv punctajul pentru cerința bonus :) .+
 </​note>​ </​note>​
  
 <note tip> <note tip>
-Pentru rezolvarea ​cerințelor ​din cadrul acestui labroator:​ +**Reamintire!!!** Puteți prezenta rezolvările ​cerințelor de până la 2 laboratoare, în fiecare săptămână. De exempluputeți prezenta laboratorul curent și pe cel din săptămâna anterioară, în totalitate sau parțial, inclusiv punctajul pentru cerința bonus :.
-  - [[https://​github.com/​UPB-Graphics/​gfx-framework-ppbg | Descărcați]] framwork-ul ​de laborator și copiați, din arhiva descărcată, directorul **Lab4**, în interiorul directorului //​gfx-framework-ppbg\src\lab//​ din versiunea voastră de proiect. +
-  - Adăugați în fișierul ''​lab_list.h''​linia ''#​include "​lab/​lab4/​lab4.h"''​. +
-  - Folosiți din nou utilitarul CMake pentru a regenera proiectul. Pentru a vă reaminti procesul de realizare a setup-uluiputeți să reconsultați [[:ppbg:​setup-framework | pagina]] dedicată acestui lucru.+
 </​note>​ </​note>​
  
-===== API-ul grafic OpenGL ​=====+===== Lanțul de transformări 3D  ​=====
  
-==== Stergerea ecranului ====+În laboratorul anterior, am văzut cum putem desena obiecte pe ecran, independent de spațiul în care am definit coordonatele vârfurilor unui obiect. Mai precis, am folosit un spațiu logic, pe care l-am denumit fereastră peste spațiul 2D și în care am definit inițial coordonatele vârfurilor. Prin utilizarea transformării fereastră-poartă,​ am putut desena obiectele într-o poartă de afișare de pe ecran, la orice rezoluție de pixeli a acestei porți. De asemenea, am văzut cum se pot prelucra obiectele prin diferite transformări. În laboratorul anterior, am analizat doar 3 tipuri de transformări de bază: translație,​ modificare de scară și rotație.
  
-Primul pas realizat la inceputul unui cadru este curatarea grilei ​de pixeli si a grilei de valori de adancime desenate la cadrul anterior. Acest proces se realizeaza in API-ul grafic OpenGL prin directiva:+===== Spațiul ​de decupare =====
  
-<code cpp> +Lucrul cu un spațiu 3D nu este cu mult diferit față de lucrul cu un spațiu 2D. Pentru desenarea pe ecran a unor obiecte dintr-un spațiu 3D, este necesar să ne stabilim un spațiu logic, din care să transformăm coordonatele vârfurilor în coordonatele grilei de pixeli. În grafica pe calculator, spațiul standard ales pentru acest proces este volumul unui cub, cu latura de dimensiune 2 și centrat în originea axelor de coordonate. Astfel, colțul stânga-jos-față al cubului are coordonatele $(-1, -1, -1)$ și colțul dreapta-sus-spate are coordonatele $(1, 1, 1)$. O reprezentare vizuală a acestui volum se poate găsi în imaginea următoare:
-glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)+
-</​code>​+
  
-Grila de valori de adancime se suprascrie cu valoarea 1, componenta z a fetei din spate pentru volumul de decupare. Culoarea cu care se suprascriu valorile in grila de pixeli se poate stabili prin:+{{ :ppbg:​laboratoare:​clip_space.png?​300 |}}
  
-<code cpp> +Coordonatele vârfurilor ce se regăsesc în acest volum pot fi aduse în grila de pixeli printr-o transformare fereastră-poartăsimilar ca săptămâna trecută:
-glClearColor(00, 0, 1); +
-</​code>​+
  
-==== Poarta de afisare ==== 
  
-Se poate stabili zona din ecran, poarta de afisare, in care sa se deseneze obiectele prin directiva: 
  
-<code cpp> 
-glViewport(0,​ 0, 1280, 720); 
-</​code>​ 
  
-==== Modele 3D ====+$$ 
 +\begin{bmatrix} 
 +x' \\ y' \\ z' \\ 1 
 +\end{bmatrix} ​= 
 + ​\begin{bmatrix} 
 +1 & 0 & 0 & V_{x}\\  
 +0 & 1 & 0 & V_{y}\\  
 +0 & 0 & 1 & 0\\ 
 +0 & 0 & 0 & 1 
 +\end{bmatrix} 
 + ​\begin{bmatrix} 
 +V_{w}/2 & 0 & 0\\  
 +0 & V_{h}/2 & 0\\  
 +0 & 0 & 1/2 & 0\\ 
 +0 & 0 & 0 & 1 
 +\end{bmatrix} 
 + ​\begin{bmatrix} 
 +1 & 0 & 0 & -1\\  
 +0 & 1 & 0 & -1\\  
 +0 & 0 & 1 & -1\\ 
 +0 & 0 & 0 & 1 
 +\end{bmatrix}
  
-Un model 3D, cunoscut in limba engleza sub numele de **3D mesh**, este un obiect tridimensional definit prin vârfuri și indici. În laborator aveți posibilitatea să încărcați modele 3D în aproape orice format posibil prin intermediul clasei [[https://​github.com/​UPB-Graphics/​gfx-framework-ppbg/​blob/​master/​src/​core/​gpu/​mesh.h#​L48|Mesh]].+\begin{bmatrix} 
 +x \\ y \\ z \\ 1 
 +\end{bmatrix} ​
  
 +$$
  
-=== Vertex Buffer Object ​(VBO===+<note tip> 
 +În cuvinte, se normalizează coordonatele vârfurilor din spațiul $([-1, 1], [-1, 1], [-1, 1])$ în spațiul $([0, viewportWidth],​ [0, viewportHeight],​ [0,1])$. 
 +</​note>​
  
-Un vertex buffer object reprezintă un container în care stocăm date ce țin de conținutul ​vârfurilor ​precum: +Observăm că se proiectează paralel coordonatele ​vârfurilor ​pe fața $z=-1$ a cubuluiPractic, se păstrează componentele $(x, y)$ ale coordonatelor din vârfuri și putem să consideram că pentru componentele $(x, y)$ se utilizează o transformare fereastră-poartă dintr-o fereastră peste spatiul 2D.
-  * poziție +
-  * normală +
-  * culoare +
-  * coordonate de texturare +
-  * etc...+
  
-Un vertex buffer object ​se poate crea prin comanda OpenGL **[[https://​www.opengl.org/​sdk/​docs/​man/​html/​glGenBuffers.xhtml|glGenBuffers]]**:  +Pentru componenta $z$, din moment ce nu se poate transforma direct în coordonatele grilei de pixeli, se normalizează între 0 și 1 și se utilizează cu scopul de valore de adâncime în procesul de rasterizare studiat în [[:ppbg:​laboratoare:​01#​testul_de_adancime ​laboratorul 1]] :) . 
-<code cpp> + 
-unsigned int VBO_ID; // ID-ul (nume sau referinta) buffer-ului ​ce va fi cerut de la GPU +==== Procesul de decupare ==== 
-glGenBuffers(1, &​VBO_ID);​ // ​se genereaza ID-ul (numele) bufferului + 
-</​code>​+Pe ecran ajung să fie desenate doar suprafețele dintr-un triunghi ​ce se regăsesc în volumul cubului ​de latură 2, centrat în originea axelor de coordonate. Cu toate acestea, este posibil ca nu toată suprafața unui triunghi să se afle în volumul cubului, astfel că în procesul de rasterizare trebuie să se realizeze procesări suplimentare pentru a nu se desena în afara grilei de pixeli. Pentru a evita realizarea acestor calcule, putem decupa triunghiurile ​la limitele volumului și să utilizăm în procesul de rasterizare doar triunghiurile rezultate în urma decupării. Scenariile în care acest mecanism produce 1, 2 și 3 triunghiuri pot fi observate în imaginea de mai jos. 
 + 
 +{{ :​ppbg:​laboratoare:​clip.png?​400 |}} 
 + 
 +Cele 1, 2 sau 3 triunghiuri,​ rezultate după procesul de decupare, sunt utilizate în procesul de rasterizare. Exemplificarea din imagine este pentru o decupare 2D, dar ea se poate extinde la volumul unui cub, în care un triunghi se decupează la limitele volumului, în spațiul 3D. 
 + 
 +Mai există un scenariu suplimentar,​ ce nu este acoperit în imagine, anume situația în care nicio parte din suprafața triunghiului nu se regăsește în interiorul volumului. În acest scenariu, nu este transmisă nicio primitivă în procesul de rasterizare.
  
 <​note>​ <​note>​
-Așa cum se poate vedea și din explicația API-uluifuncția [[https://​www.opengl.org/​sdk/​docs/​man/​html/​glGenBuffers.xhtml|glGenBuffers]] primește numărul de buffere ce trebuie generate cât și locația din memorie unde vor fi salvate referințele (ID-urile) generate.\\  +Datorită faptului că acest proces de decupare are loc în spațiul finit descris mai sus, determinat de un cub de latură 2, centrat în originea axelor de coordonateacest spațiu poartă numele ​de **spațiu de decupare**. El este cunoscut ​în limba engleză sub numele de **clip space**.
-În exemplul ​de mai sus este generat doar 1 singur buffer iar ID-ul este salvat ​în variabila ''​VBO_ID''​.+
 </​note>​ </​note>​
  
-Pentru a distruge un VBO și astfel să eliberăm memoria de pe **GPU** se folosește comanda **[[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glDeleteBuffers.xhtml|glDeleteBuffers]]**:​ +===== Transformarea de proiecție perspectivă =====
-<code cpp> +
-glDeleteBuffers(1,​ &​VBO_ID);​ +
-</​code>​+
  
-Pentru a putea pune date într-un buffer trebuie întâi ​să legăm acest buffer la un „target”Pentru un vertex buffer acest „binding point” ​se numește **GL_ARRAY_BUFFER** și se poate specifica prin comanda **[[https://​www.khronos.org/​opengles/​sdk/​1.1/​docs/​man/​glBindBuffer.xml|glBindBuffer]]**:​+Definirea unui spațiu finit pe care să îl normalizăm ulterior în grila de pixeli ne oferă o flexibilitate foarte mareDezavantajul aplicării directe a acestei abordări este că geometria obiectelor nu se regăsește ​întotdeauna în interiorul volumului de decupare. De fapt, de cele mai multe ori, obiectele nu se află în acest volumAstfel, trebuie să aducem noi geometria în acesta printr-o transformare suplimentarăAceastă transformare poate să fie o transformare de proiecție perspectivă pentru a simula procesul de transport al luminii de pe suprafețele scenei la observator.
  
-<code cpp> +Tot ce „vede” ochiul uman este luminaastfel că procesul de desenare a suprafețelor unui obiect pe ecran simulează procesul de achiziție a luminii care ajunge de pe suprafețele obiectului la ochiul uman :. Pentru simplitate, vom considera un singur punct de convergență pentru simularea razelor de lumină. Astfel, putem să folosim o transformare perspectivă a coordonatelor vârfurilor pentru a aduce geometria în spațiul de decupare și ulterior pentru a o desena pe ecran.
-glBindBuffer(GL_ARRAY_BUFFERVBO_ID)+
-</​code>​+
  
-În acest moment putem să facem upload ​de date din memoria **CPU** către **GPU** prin intermediul comenzii **[[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glBufferData.xhtml|glBufferData]]**:​+Volumul proiecției perspectivă este un trunchi ​de piramidă, frustumStandard, acest volum este descris cu vârful în originea axelor de coordonate și cu baza orientată de-a lungul axei $Z$, în sens negativAcest volum se normalizează în volumul spațiului de decupare. O reprezentare vizuală a lui se găsește în imaginea de mai jos.
  
-<code cpp> +{{ :​ppbg:​laboratoare:​perspective.png?500 |}}
-glBufferData(GL_ARRAY_BUFFER,​ sizeof(vertices[0]) * vertices.size(), &​vertices[0],​ GL_STATIC_DRAW);​ +
-</​code>​+
  
-  * Comanda citește de la adresa specificată, în exemplul ​de sus fiind adresa primului vârf ''&​vertices[0]''​și copiază ​în memoria video dimensiunea specificată prin parametrul al 2-lea+Avem mai multe posibilități de a descrie forma acestui volumdar în continuare va fi prezentată cea care apare uzual în grafica pe calculatorPentru descrierea frustumului folosim: 
-  * **GL_STATIC_DRAW** reprezintă un hint pentru driver-ul video în ceea ce privește metoda de utilizare ​bufferului. Acest simbol poate avea mai multe valori dar în cadrul laboratorului este de ajuns specificarea prezentată. Mai multe informații găsiți pe pagina ​de manual ​funcției [[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glBufferData.xhtml|glBufferData]]+  * Distanța până la un plan apropiat, pe care se va normaliza fața $z=-1$ a spațiului de decupare (**near**
 +  * Distanța până la un plan îndepărtat,​ pe care se va normaliza faț$z=1$ a spațiului ​de decupare (**far**) 
 +  * Un unghi pentru deschiderea verticală a piramidei (**fovy**) 
 +  * Un raport de aspect al porții de afișare, lățime / înălțime,​ cu ajutorul căruia se poate obține unghiul ​de deschidere orizontală ​piramidei (**aspect**)
  
 +Formula de calcul a proiecției perspective este:
 +
 +
 +
 +
 +$$
 +x' = \frac{x}{ -z \cdot tan(\frac{fovy}{2})\cdot aspect}\\
 +y' = \frac{y}{ -z \cdot tan(\frac{fovy}{2})}\\
 +z' = \frac{c2\cdot z-c1}{-z}\\ c1=\frac{(-2) \cdot far \cdot near}{far- near}\\ c2=\frac{(-1)\cdot(far+near)}{far - near}
 +$$
 +
 +
 +Această formulă nu poate fi scrisă sub formă matriceală,​ deoarece este un sistem de ecuații neliniare. După cum vedem, în calcularea componentelor $(x', y', z')$ ale coordonatei obținute în urma transformării de proiecție perspectivă,​ se utilizează valoarea componentei $z$ a coordonatei pentru care se aplică transformarea. Din acest motiv, este necesar să realizăm acest proces în 2 pași:
 +  - Se creează o matrice pentru tranformarea de proiecție perspectivă prin care se aplică formula de mai sus, fără împărțirea componentelor coordonatei rezultate în urma transformării la valoarea componentei $z$ a coordonatei pentru care s-a realizat transformarea. Se utilizează această matrice pentru transformarea de proiecție perspectivă $$
 +\begin{bmatrix}
 +{x}'​\\ ​
 +{y}'​\\ ​
 +{z}'​\\ ​
 +{w}'
 +\end{bmatrix} = \begin{bmatrix}
 +\frac{1}{tan(\frac{fovy}{2})\cdot aspect} & 0 & 0 & 0 \\
 +0 & \frac{1}{tan(\frac{fovy}{2})} & 0 & 0 \\ 
 +0 & 0 & \frac{(-1)\cdot (far+near)}{far - near} & \frac{(-2) \cdot far \cdot near}{far - near} \\ 
 +0 & 0 & -1 & 0
 +\end{bmatrix}
 +
 +\begin{bmatrix}
 +x\\ 
 +y\\ 
 +z\\ 
 +1
 +\end{bmatrix} ​
 +$$
 +  - Împărțirea componentelor $(x', y', z')$ cu valoarea componentei $z$ a coordonatei vârfului pentru care s-a aplicat transformarea
 +
 +
 +
 +$$
 +x''​ = \frac{x'​}{-z}\\
 +y''​ = \frac{y'​}{-z}\\
 +z''​ = \frac{z'​}{-z}
 +$$
 +
 +Pentru a nu fi nevoie să se rețină apriori valoarea componentei $z$, care poate fi obținută în urma aplicării unui lanț de transformări,​ astfel că transformarea de proiecție perspectivă se poate afla în interiorul unei matrici compuse dintr-un lanț de transformări,​ se utilizează un artificiu. Se poate observa că matricea de mai sus este construită astfel încât să păstreze în componenta $w'$ , valoarea componentei $z$, negativă:
 +
 +
 +
 +$$
 +w'​=0\cdot x + 0\cdot y + (-1) \cdot z + 0 \cdot 0
 +$$
 +
 +Din acest motiv, cel de-al doilea pas de mai sus devine:
 +
 +
 +
 +$$
 +x''​ = \frac{x'​}{w'​}\\
 +y''​ = \frac{y'​}{w'​}\\
 +z''​ = \frac{z'​}{w'​}
 +$$
 +
 +<​note>​
 +Acest al doilea pas este cunoscut sub numele de **împărțire perspectivă** și este implementat nativ în banda grafică a procesorului. Observăm că această transformare este ultima care se aplică înainte de transformarea coordonatelor din vârfuri în spațiul de decupare. Din acest motiv, înainte de decuparea propriu-zisă,​ procesorul grafic realizează împărțirea perspectivă automat, hardware sau software în driver-ul companion al procesorului grafic.
 +</​note>​
  
 <note tip> <note tip>
-Pentru a înțelege mai bine API-ul OpenGL vă recomandăm să citițdocumentația indicată pentru fiecare comandă prezentată. Atunci când se prezintă o nouă comandădacă apăsați click pe numele ​acesteia veți fi redirecționați către pagina ​de manual a comenzii respective.\\  +Spațiul finit determinat de volumul trunchiului de piramidă ce are vârful în originea axelor de coordonate șbaza orientata de-a lungul axei $Z$, în sens negativpoartă numele ​de **spațiu de vizualizare**În limba englezăacest spațiu se regăsește sub numele de **view space**.
-De asemeneadocumentația oficială și completă a API-ului OpenGL poate fi gasită pe pagina ​**[[https://​www.opengl.org/​sdk/​docs/​man/​|OpenGL 4 Reference Pages]]** +
 </​note>​ </​note>​
  
-=== Index Buffer Object (IBO) ===+<​note>​ 
 +Formula de calculare a proiecției perspective pentru coordonata unui vârf, pe planul din apropiere a trunchiului de piramidă, se obține din următoarea observație:​ conform imaginii de mai jos, se poate vedea că triunghiul determinat de vectorii marcați cu $near$ și $y'$ este asemenea cu cel determinat de vectorii marcați cu $-z$ și $y$.
  
-Un index buffer object (numit și element buffer object) reprezintă un container în care stocăm indicii vertecșilor. Cum **VBO** si **IBO** sunt buffere, ele sunt extrem de similare în construcție,​ încărcare de date și ștergere.+{{ :​ppbg:​laboratoare:​perspective2.png?600 |}}
  
-<code cpp> +Astfelconform teoremei lui Thalesrezultă că:
-glGenBuffers(1&​IBO_ID);​ +
-glBindBuffer(GL_ELEMENT_ARRAY_BUFFERIBO_ID); +
-glBufferData(GL_ELEMENT_ARRAY_BUFFER,​ sizeof(indices[0]) * indices.size(),​ &​indices[0],​ GL_STATIC_DRAW);​ +
-</​code>​+
  
-La fel ca la VBO, creăm un IBO și apoi îl legăm la un punct de legatură, doar că de data aceasta punctul de legatură este **GL_ELEMENT_ARRAY_BUFFER**. Datele sunt trimise către bufferul mapat la acest punct de legatură. În cazul indicilor toți vor fi de dimensiunea unui singur întreg.+$$ 
 +\frac{y'​}{near} = \frac{y}{-z}\\
  
-=== Vertex Array Object (VAO) ===+$$
  
-Într-un vertex array object putem stoca toată informația legată de starea geometriei desenate. Putem folosi un număr mare de buffere pentru a stoca fiecare din diferitele atribute („separate buffers”). Putem stoca mai multe (sau toate) atribute într-un singur buffer („interleaved” buffers). În mod normal înainte de fiecare comandă de desenare trebuie specificate toate comenzile de „binding” pentru buffere sau atribute ce descriu datele ce doresc a fi randate. Pentru a simplifica această operație se folosește un vertex array object care ține minte toate aceste legături. 
  
-Un vertex array object este creat folosind comanda **[[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glGenVertexArrays.xhtml|glGenVertexArrays]]**:​ 
  
-<code cpp> +$$ 
-unsigned int VAO; + 
-glGenVertexArrays(1, &VAO); +y'​=\frac{near\cdot y}{-z}\\ 
-</​code>​+$$ 
 + 
 + 
 + 
 +Considerăm că valoarea componentei $z$ este negativă, deoarece o tratăm pe post de distanță și orientarea bazei trunchiului de piramidă este din construcție de-a lungul axei $Z$, în sens negativ. 
 + 
 +Suplimentar,​ presupunem că avem distanța până la limita de jos, denumită **bottom** și distanța până la limita de sus, denumtiă **top**, a feței din apropiere a frustumului,​ după cum se poate vedea în imaginea de mai sus. Dorim să normalizăm componenta $y$ din intervalul $[bottom, top]$ în $[-1, 1]$. Aceasta este o transformare fereastră-poartă. Împreună cu formula anterioara pentru $y'$, obținem că $y'$ este: 
 + 
 + 
 +$$ 
 +y'​=\frac{2\cdot near\cdot y}{-z \cdot (top-bottom)}\\ 
 +$$ 
 + 
 +Din construcție,​ avem un singur unghi de deschidere verticală a trunchiului de piramidă, astfel că avem aceeași valoare pentru limitele bottom și top: 
 + 
 + 
 + 
 +$$ 
 +bottom= -top\\ 
 +y'​=\frac{2\cdot near\cdot y}{-z \cdot 2 \cdot top}=\frac{near\cdot y}{-z \cdot top}\\ 
 +$$ 
 + 
 +Se poate observa că: 
 + 
 + 
 +$$ 
 +\frac{top}{near}=tan(\frac{fovy}{2}) 
 +$$ 
 + 
 +Astfel, avem pentru $y'​$:​ 
 + 
 +$$ 
 + 
 +y' = \frac{y}{ -z \cdot tan(\frac{fovy}{2})}\\ 
 +$$ 
 + 
 +Pentru simplitate și un control ridicat, în loc să se descrie volumul frustumului prin 2 valori, una care descrie unghiul de deschidere verticală și una pentru unghiul de deschidere orizontală,​ se utilizează raportul de aspect al ecranului:​ 
 + 
 + 
 +$$ 
 + 
 +aspect = \frac{V_{w}}{V_h}\\ 
 +x' = \frac{x}{ -z \cdot tan(\frac{fovy}{2})\cdot aspect}\\ 
 +$$ 
 + 
 +Daca formula pentru $(x, y)$ reiese din proiecția coordonatelor vârfului pe planul din apropiere al trunchiului de piramidă, formula pentru componenta $z$ este artificială și reiese exclusiv din dorința de a creea o normalizare **neliniară** între distanța planului din apropiere și distanța planului îndepărtat. Se dorește ca pentru obiectele din apropiere de planul apropiat, $z=-near$, să fie alocat un interval mult mai mare în spațiul de decupare, decât pentru obiectele apropiate de planul $z=-far$. Din acest motiv, formula standard aleasă inițial este: 
 + 
 + 
 + 
 +$$ 
 + 
 +z'​=\frac{c1}{-z}+c2 
 +$$ 
 + 
 +Parametrii $c1$ și $c2$ sunt cei din formula originală. Problema acestei formule este că nu se imparte la valoarea $z$, ceea ce înseamnă că pentru $z'$ nu s-ar aplica împărțirea perspectivă. Pentru a face uniform procesul de împărțire perspectivă,​ se modifică formula și obținem versiunea finală: 
 + 
 + 
 + 
 + 
 +$$ 
 + 
 +z'​=\frac{c2\cdot z-c1}{-z} 
 +$$
  
-Este legat cu **[[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glBindVertexArray.xhtml|glBindVertexArray]]**: ​ 
  
-<code cpp>​glBindVertexArray(VAO);</​code>​ 
-<​hidden>​ 
-Și este distrus cu **[[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glDeleteVertexArrays.xhtml|glDeleteVertexArrays]]**: ​ 
-<code cpp>​glDeleteVertexArrays(1,​ &​VAO);</​code>​ 
-</​hidden>​ 
-<note tip> 
-Înainte de a crea VBO-urile și IBO-ul necesar pentru un obiect se va lega VAO-ul obiectului și acesta va ține minte automat toate legăturile specificate ulterior. 
  
-După ce toate legăturile au fost specificate este recomandat să se dea comanda ''​glBindVertexArray(0)''​ pentru a dezactiva legătura către VAO-ul curent, deoarece altfel riscăm ca alte comenzi OpenGL ulterioare să fie legate la același VAO și astfel să introducem foarte ușor erori în program. ​ 
 </​note>​ </​note>​
  
-Înainte ​de comanda de desenare este suficient să legăm doar VAO-ul ca OpenGL să știe toate legatările create la construcția obiectului.+===== Transformarea ​de vizualizare =====
  
-==== Optiunea ​de optimizare Face Culling====+Prin transformarea ​de proiecție perspectivă,​ descrisă mai sus, putem modifica coordonatele vârfurilor rețelei de triunghiuri pentru a le așeza în spațiul de decupare și de acolo putem desena triunghiurile pe ecran. Cu toate acestea, coordonatele obiectelor noastre nu se regăsesc întotdeauna în spațiul de vizualizare,​ reprezentat,​ cum s-a menționat mai sus, de volumul unui trunchi de piramidă cu vârful în originea axelor de coordonate și cu baza orientată de-a lungul axei $Z$, în sens negativ. În situația în care dorim să desenăm scena dintr-un alt punct de vedere, trebuie să definim conceptul de observator. Proprietățile care definesc un observator sunt: 
 +  * O poziție $P$ în spatiu, de unde „privește” obiectele din scenă; 
 +  * O direcție $\vec{F}$ în care „privește” obiectele. Companion acestei direcții, mai este necesar un vector $\vec{R}$ ce descrie direcția dreaptă a observatorului,​ daca iși rotește direcția în care priveste scena cu 90 de grade față de axa OY și un vector $\vec{U}$ ce descrie direcția sus a observatorului. Cei 3 vectori sunt ortogonali, mai exact, unghiul dintre oricare 2 vectori din cei 3 este de 90 de grade.
  
-API-ul OpenGL oferă posibilitatea de a testa orientarea aparentă pe ecran a fiecărui triunghi înainte ca acesta să fie redat și să îl ignore ​în funcție de starea ​de discard setată: **GL_FRONT** sau **GL_BACK**Acestă funcționalitate poartă numele ​de **[[https://​www.opengl.org/​wiki/​Face_Culling|Face Culling]]** și este foarte importantă deoarece reduce costul de procesare total+Pe baza acestor informații,​ putem să creăm o transformare care modifică obiectele din scenă, din spațiul în care se află, într-un spațiu ​în care observatorul are poziția în originea axelor ​de coordonate și „privește” scena de-a lungul axei $Z$, în sens negativPeste acest spațiu se poate suprapune volumul ​de vizualizare,​ utilizat pentru transformarea proiecției perspective.
  
-Modul cum este considerată o față ca fiind **GL_FRONT** sau **GL_BACK** poate fi schimbat folosind comanda [[https://​www.khronos.org/​registry/​OpenGL-Refpages/​gl4/​html/​glFrontFace.xhtml|glFrontFace]] (valoarea inițială pentru o față **GL_FRONT** este considerată ca având ordinea specificării vârfurilor în sens trigonometric / counter clockwise): +Transformarea de vizualizare arată în felul următor:
-<code cpp> +
-// mode can be GL_CW (clockwise) or GL_CCW (counterclockwise) +
-// the initial value is GL_CCW +
-void glFrontFace(GLenum mode​); +
-</​code>​+
  
-<​note>​ + 
-Exemplupentru un **cub** maxim 3 fețe pot fi vizibile la un moment dat din cele 6 existente. În acest caz maxim 6 triunghiuri vor fi procesate pentru afișarea pe ecran în loc de 12.+$$ 
 +\begin{bmatrix} 
 +{x}'\\  
 +{y}'\\  
 +{z}'\\  
 +
 +\end{bmatrix} = \begin{bmatrix} 
 +\vec{R}_x & \vec{R}_y & \vec{R}_z & 0 \\ 
 +\vec{U}_x & \vec{U}_y & \vec{U}_z & 0 \\  
 +\vec{F}_x & \vec{F}_y & \vec{F}_z & 0 \\  
 +0 & 0 & 0 & 1 
 +\end{bmatrix} 
 + ​\begin{bmatrix} 
 +1 & 0 & 0 & -P_x \\ 
 +0 & 1 & 0 & -P_y \\  
 +0 & 0 & 1 & -P_z \\  
 +0 & 0 & 0 & 1 
 +\end{bmatrix} 
 + 
 +\begin{bmatrix} 
 +x\\  
 +y\\  
 +z\\  
 +
 +\end{bmatrix}  
 +$$ 
 + 
 + 
 +Se poate observa că prima transformare aplicată, ultima în lanțul de înmulțiri de matrici, este o translație inversă a coordonatelor poziției observatorului în spațiul 3D, similară cu cea discutată în laboratorul anterior pentru spațiul 2D. A doua transformare aplicată este o rotație. Pentru construcția acestei matrici s-a folosit [[https://​ro.wikipedia.org/​wiki/​Procedeul_Gram%E2%80%93Schmidt | procedeul Gram–Schmidt]]. 
 + 
 +<​note ​tip
 +Spațiul în care se regăsescpoziția observatorului,​ vectorii ce descriu direcția de „privire”,​ direcția dreapta și sus, împreună cu coordonatele tuturor obiectelor „privite” de către observator, poartă numele de **spațiu al lumii**. În limba engleză, se întâlnește denumirea ​de **world space**.
 </​note>​ </​note>​
  
-În mod normal face-culling este dezactivat. Acesta poate fi activat folosind comanda [[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glEnable.xhtml|glEnable]]:​ +===== Transformarea de modelare =====
-<code cpp> +
-glEnable(GL_CULL_FACE);​ +
-</​code>​+
  
-Pentru ​dezactiva face-culling se folosește comanda [[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glEnable.xhtml|glDisable]]:​ +Coordonatele vârfurilor din rețeaua de triunghiuri ce descrie suprafațunui obiect se declară inițial într-un spațiu convenabil creării acelui obiect.
-<code cpp> +
-glDisable(GL_CULL_FACE);​ +
-</​code>​+
  
-Pentru a specifica ce orientare ​fețelor să fie ignorată se folosește comanda **[[https://​www.opengl.org/​wiki/​GLAPI/​glCullFace|glCullFace]]** +Pentru a modifica obiectul și a-l așeza sub forma dorită în punctul final din scenăse folosesc transformările de translație,​ de modificare a scării ​și de rotațiediscutate în laboratorul anterioradaptate pentru un spațiu 3D.
-<code cpp> +
-// GL_FRONTGL_BACKand GL_FRONT_AND_BACK are accepted. +
-// The initial value is GL_BACK. +
-glCullFace(GL_BACK);​ +
-</​code>​+
  
-===== Aplicatiile grafice in timp real =====+==== Transformarea de translație ​====
  
-Aplicatiile grafice in timp real realizeaza o desenare succesiva ​cate unui cadru. Intre 2 cadre succesive, parametrii ​de desenare, precum pozitia si directia ​de vizualizare ​observatorului,​ pozitia geometriei desenate sau alte elemente ce influenteaza desenare, cum este informatii ​unei surse de lumina, pot sa difere. Un numar mare de cadre desenate pe secunda, creeaza iluzia de animatie continua.+Forma matriceală ​transformărilor ​de translație și de modificare ​scării ​este similară cu cea a transformărilor utilizate într-un spațiu 2D. Fiecare din cele două matrici se modifică doar prin adăugarea ​unei noi linii și a unei noi coloane pentru a adapta cea de-a treia dimensiune ce apare într-un spațiu 3D.
  
-In framework-ul pus la dispozitie in cadrul acestui laborator, aceasta succesiune de cadre se realizeaza in clasa ''​world''​ in metoda ''​Run''​ printr-o bucla care se opreste doar in momentul in care se inchide fereastra. 
  
-<code cpp> 
-while (!window->​ShouldClose()) 
-{ 
-    LoopUpdate();​ 
-} 
-</​code>​ 
  
-==== Analiza unui cadru ====+$$ 
 +\begin{bmatrix} 
 +{x}'\\  
 +{y}'​\\ 
 +{z}'\\  
 +
 +\end{bmatrix} ​\begin{bmatrix} 
 +1 & 0 & 0 & t_x\\  
 +0 & 1 & 0 & t_y\\  
 +0 & 0 & 1 & t_z\\  
 +0 & 0 & 0 &1 
 +\end{bmatrix}
  
-In interiorul acestei bucle, in interiorul metodei ''​LoopUpdate''​ se realizeaza urmatorul proces pentru fiecare cadru:+\begin{bmatrix} 
 +x\\  
 +y\\  
 +z\\  
 +
 +\end{bmatrix}  
 +$$
  
-<code cpp> 
-// 1. Polls and buffers the events 
-window->​PollEvents();​ 
  
-// 2. Computes frame deltaTime in seconds +==== Transformarea de modificare a scării ====
-ComputeFrameDeltaTime();​+
  
-// 3. Calls the methods of the instance of InputController in the following order 
-// OnWindowResize,​ OnMouseMove,​ OnMouseBtnPress,​ OnMouseBtnRelease,​ OnMouseScroll,​ OnKeyPress, OnMouseScroll,​ OnInputUpdate 
-// OnInputUpdate will be called each frame, the other functions are called only if an event is registered 
-window->​UpdateObservers();​ 
  
-// 4. Frame processing +$$ 
-FrameStart();​ +\begin{bmatrix} 
-Update(static_cast<​float>​(deltaTime));​ +{x}'​\\ ​ 
-FrameEnd();+{y}'​\\ 
 +{z}'\\  
 +
 +\end{bmatrix} = \begin{bmatrix} 
 +s_x & 0 & 0 &0 \\  
 +0 & s_y & 0 &0 \\  
 +0 & 0 & s_z &0 \\ 
 +0 & 0 & 0 &1  
 +\end{bmatrix}
  
-// 5. Swap front and back buffers - image will be displayed to the screen +\begin{bmatrix} 
-window->​SwapBuffers();​ +x\\  
-</​code>​+y\\  
 +z\\  
 +
 +\end{bmatrix}  
 +$$
  
-Pasii din codul de mai sus sunt: 
-  - Fereastra in interfata grafica a sistemului de operare este creata prin intermediul bibliotecii GLFW. Tot prin aceasta biblioteca obtinem evenimente externe aplicatiei care se pot produce la nivelul ferestrei, precum apasarea de catre utilizator a unei taste sau a unui buton de la mouse, redimensionarea ferestrei sau inchiderea ei. Vom reveni asupra acestor evenimente mai jos. 
-  - Se calculeaza timpul de desenare a cadrului anterior, denumit ''​deltaTime''​. 
-  - In framework-ul de laborator exista metode in clasele ''​LabX''​ pentru a trata fiecare tip de eveniment. Acestea sunt discutate mai jos. 
-  - Metode ce se apeleaza exast o data per cadru. 
-  - Se blocheaza procesul curent de pe CPU pentru a se astepta incheierea tuturor proceselor de desenare realizate de catre procesorul grafic. 
  
-==== Interactiunea cu utilizatorul ​====+==== Transformarea de rotație ​====
  
-Interactiunea utilizatorului ​cu fereastra este realizata ​+Mecanismul de rotație, studiat în laboratorul trecut, rotește o coordonată a unui punct într-un spațiu 2D cu un unghi a, //în sens trigonometric față de originea axelor de coordonate//​. Acest mecanism NU poate fi adaptat direct la un spațiu 3D.
  
-Interactiunea utilizatorului cu tastele de la tastaturain situatia in care fereastra este selectate. Aceasta interactiune poate fi de 3 feluri: +Astfeltransformarea ​de rotație dintr-un spațiu 2D este utilizată într-un spațiu 3D pentru ​a roti o coordonată într-un singur plan din spațiul 3D. Mai exact într-un plan paralel cu unul din cele 3 planuri ce trec prin originea axelor de coordonate. Există câte o matrice de transformare diferită pentru fiecare din cele 3 planuri. În situația în care dorim sa creăm o transformare ​de rotație într-un plan oarecaretrebuie să o obținem prin compunerea din mai multe transformări ​de bază.
-  ​Apasarea unei taste pentru ​prima data in cadrul curent, cunoscuta in limba engleza sub numele de **key press** +
-  ​Neapasarea unei taste la cadrul curent, in situatia in care tasta a fost apasata la cadrul anterior, cunoscuta in limba engleza sub numele ​de **key release** +
-  ​Apasarea unei taste la cadrul curentindiferent cand a fost apasata prima data, cunoscuta in limba engleza sub numele ​de **key hold**+
  
-Interactiunea utilizatorului cu butoanele si rotita de la mouse. Pentru butoanele de la mouseinteractiunea poate fi de 3 feluri, similar ca in situatia tastelor.+Astfelavem transformarea ​de rotație într-un plan paralel cu planul XOY:
  
-In framework-ul de laborator, interactiunea cu utilizatorul se realizeaza in metodele ​''​OnWindowResize'​', ''​OnMouseMove'​', ''​OnMouseBtnPress'​', ​''​OnMouseBtnRelease''​''​OnMouseScroll''​''​OnKeyPress''​, ''​OnMouseScroll'', ​ce sunt apelate pentru fiecare eveniment realizat intr-un cadruDe exemplu:+ 
 +$$ 
 +\begin{bmatrix} 
 +{x}'\\  
 +{y}'\\  
 +{z}'\\  
 +
 +\end{bmatrix} = \begin{bmatrix} 
 +cos(u) & -sin(u) & 0 & 0\\  
 +sin(u) & cos(u) & 0 & 0\\  
 +0 & 0 & 1 & 0 \\ 
 +0 & 0 & 0 & 1 
 +\end{bmatrix} 
 + 
 +\begin{bmatrix} 
 +x\\  
 +y\\  
 +z\\  
 +
 +\end{bmatrix}  
 +$$ 
 + 
 + 
 +Putem să considerăm că această transformare este față de axa OZ. 
 + 
 + 
 +Transformarea de rotație într-un plan paralel cu planul XOZsau față de axa OY este: 
 + 
 + 
 + 
 + 
 +$$ 
 +\begin{bmatrix} 
 +{x}'\\  
 +{y}'\\  
 +{z}'\\  
 +
 +\end{bmatrix} = \begin{bmatrix} 
 +cos(u) & 0 & sin(u) & 0\\  
 +0 & 1 & 0 & 0 \\ 
 +-sin(u) & 0 & cos(u) & 0\\  
 +0 & 0 & 0 & 1 
 +\end{bmatrix} 
 + 
 +\begin{bmatrix} 
 +x\\  
 +y\\  
 +z\\  
 +
 +\end{bmatrix}  
 +$$ 
 + 
 + 
 + 
 + 
 +Transformarea de rotație într-un plan paralel cu planul YOZsau față de axa OX este: 
 + 
 +$$ 
 +\begin{bmatrix} 
 +{x}'\\  
 +{y}'\\  
 +{z}'\\  
 +
 +\end{bmatrix} = \begin{bmatrix} 
 +1 & 0 & 0 & 0 \\ 
 +0 & cos(u) & -sin(u) & 0 \\  
 +0 & sin(u) & cos(u) & 0 \\  
 +0 & 0 & 0 & 1 
 +\end{bmatrix} 
 + 
 +\begin{bmatrix} 
 +x\\  
 +y\\  
 +z\\  
 +
 +\end{bmatrix}  
 +$$ 
 + 
 + 
 +<note tip> 
 +Spațiul în care sunt definite inițial coordonatele vârfurilor poartă numele de **spațiu al obiectului**sau în limba englezăpoate fi întâlnit cu denumirea de **object space**. 
 +</​note>​ 
 + 
 +===== Eliminarea din procesul de rasterizare a triunghiurilor obturate ===== 
 + 
 +O optimizare suplimentarăpe care o putem realiza în spațiul de decuparepe lângă cea de decupare propriu-zisă,​ este să excludem din procesul de rasterizare triunghiurile care sunt complet obturate de altă geometrie. Pentru a nu face o procesare avansată la nivel de rețea de triunghiuri,​ putem să folosim următorul artificiu:​ 
 + 
 +În situția în care desenăm un model 3D etanș, care nu conține nicio gaură în suprafața lui, avem proprietatea că din orice punct am privi geometria, nu îi vom putea vedea niciodată interiorul. Astfel, avem următoarea formă de definire a unui triunghi: pe baza descrierii cu indici din ''​vertices'', ​utilizată în laboratoarele anterioare, sensul în care sunt afișate vârfurile, pe baza ordinii din indici, ne determină nouă faptul că privim fațeta față sau fațeta spate a triunghiului. 
 + 
 +Mai exact, dacă avem descrisă urmatoarea geometrie:
  
 <code cpp> <code cpp>
 +vector<​VertexFormat>​ vertices
 +{
 +    VertexFormat(glm::​vec3(0,​ 2, 0)),
 +    VertexFormat(glm::​vec3(-1,​ 0, 0)),
 +    VertexFormat(glm::​vec3(1,​ 0, 0)),
 +}
  
-void Lab4::​OnKeyPress(int key, int mods)+vector<​unsigned ​int> indices1
 { {
-    ​if (key == GLFW_KEY_R) { +    ​0, 1, 2,
-        printf("​S-a apasat tasta R"); +
-    }+
 } }
 +
 +vector<​unsigned int> indices2
 +{
 +    0, 2, 1,
 +}
 +
 </​code>​ </​code>​
  
-Metoda ''​OnKeyPress'' ​se apeleaza pentru fiecare tasta apasata intr-un cadru. Trebuie verificat in interiorul metodeipe baza parametrilor ce tasta a fost apasataPentru ​mai multe informatii despre fiecare metoda in parteva rog sa cititi detaliile din [[https://​github.com/​UPB-Graphics/​gfx-framework-ppbg/​blob/​master/​src/​core/​window/​input_controller.fisierul header al clasei InputController]].+În momentul în care geometria ​se află în spațiul de decuparedupă aplicarea tuturor transformărilor,​ se observă sensul dat de coordonatele vârfurilor,​ în ordinea în care au fost definiți indiciiÎn situația în care sensul pe care îl fac coordonatele,​ conform imaginii de mai joseste trigonometric,​ considerăm că ne uităm la fațeta față a triunghiului. 
 + 
 +{{ :ppbg:​laboratoare:​cull.png?​400 ​|}} 
 + 
 +Prin acest proces, în situația în care desenăm suprafața unui model 3D etanș, putem exclude din procesul de rasterizare toate triunghiurile pentru care afișăm fațetele spate, deoarece avem garanția că ele sunt obturate de restul geometriei modelului.
  
 <note tip> <note tip>
-Apelul fiecarei metode ​se realizeaza in cadrul pasului 4 descris mai sus.+Acest proces ​se întâlnește în limba engleză sub numele de **face culling**, adică tăierea de la desenare a anumitor triunghiuri,​ pe baza fațetei pe care o vedem. Avem opțiunea de a tăia de la desenare triunghiurile pentru care afișăm fațetele față sau pe cele pentru care afișăm fațetele spate.
 </​note>​ </​note>​
  
-Metoda ''​OnInputUpdate''​ are un statut special in cadrul framework-ului ​de laborator si este similara cu metoda ''​Update'',​ mai exact este apelata in fiecare cadru exact o data. Apelul ei se realizat in cadrul pasului 4 de mai sus, astfel ca se apeleaza inainte de metoda ''​Update''​. Recomandarea este sa utilizati aceasta metoda cand gestionati interactiunea cu utilizatorul.+===== Privire ​de ansamblu =====
  
-===== Cerinte laborator =====+O privire de ansamblu a întrgului proces se poate regăsi în imaginea următoare:
  
-<​note ​important+{{ :​ppbg:​laboratoare:​overview.png?​600 |}} 
-Pentru toate cerintele in care se precizeaza ca animatiile trebuie sa fie continueutilizati valoarea ''​deltaTime''​.+ 
 +  - Din //spațiul obiectului//,​ prin **transformarea de modelare**, ce conține un lanț de transformări de translație,​ de modificare a scării și de rotație, obiectele ajung în //spațiul lumii//; 
 +  - Din //spațiul lumii//, prin **transformarea de vizualizare**,​ conform proprietăților observatorului,​ obiectele ajung în //spațiul de vizualizare//;​ 
 +  - Peste //spațiul de vizualizare//​ se aplică volumul de proiecție perspectivă,​ reprezentat de un trunchi de piramidă, centrat în originea axelor de coordonate, cu baza orientată de-a lungul axei Z, în sens negativ. Din acest spațiu, prin **transformarea de proiecție** perspectivă,​ obiectele ajung în //spațiul de decupare//, reprezentat de volumul unui cub, cu latura de dimensiune 2, centrat în originea axelor de coordonate. 
 +  - După procesul de decupare și de eliminare a triunghiurilor obturate, în situația în care dorim acest al doilea proces, prin transformarea coordonatelor din volumul de decupare în poarta de afișare, triunghiurile se transmit la procesul de rasterizare,​ prin care se desenează suprafețele triunghiurilor în grila de pixeli :) . 
 + 
 +<​note ​tip
 +Felicitări :) ! Daca ați înțeles acest proces, ați înțeles standardul ​care stă la baza graficii pe calculator în timp real. Toate procesoarele grafice implementează acest standardhardware sau software în driver-ul companion procesorului. Acest fapt este valabil și pentru arhitecturile ce implementează hardware procesul de ray-tracing. Aplicațiile grafice ce utilizează această arhitectură folosesc o abordare hibridă ce combină banda grafică de rasterizare cu banda grafică de ray-tracing.
 </​note>​ </​note>​
  
-  - 0.05p - Completați metoda ''​CreateMesh()''​ astfel încât să încărcați geometria in memoria RAM a procesorului grafic. +===== Cerințe laborator =====
-    * Creați un VAO +
-    * Creați un VBO și adăugați date în el +
-    * Creați un IBO și adăugați date în el +
-    * Dupa completarea corecta a metodei, rezultatul vizual ar trebui sa fie urmatorul: {{ :​ppbg:​laboratoare:​result1.png?​600 |}} +
-  - 0.05p - La apasarea unei taste, alegeti o culoarea aleatoare pentru curatarea grilei de pixeli +
-    * Utilizati in metoda ''​Update()''​ directiva de specificare a culorii curatare a informatiei de pe ecran. +
-    * La apasarea tastei R, alegeti o culoare aleatoare +
-  - 0.05p - La apasarea unei taste, +
-    * Utilizati in metoda ''​DrawObjects()''​ directivere de activare si dezactivare a optimizarii Face culling +
-    * Utilizati in metoda ''​DrawObjects()''​ directiva de specificare a tipului de fata pentru care triunghiurile se elimina din procesul de rasterizare +
-    * La apasarea tastei F, schimbati intre eliminarea triunghiurilor pentru care se afiseaza fateta fata sau spate +
-  - 0.05p - Pentru cele 3 cuburi din scena aplicati urmatoarele animatii: +
-    * Unul dintre cuburi sa se deplaseze continuu sus-jos intre limitele pe axa y: 0 si 3 +
-    * Un alt cub sa se roteasca continuu fata de una dintre cele 3 axe principale +
-    * Un alt cub sa pulseze continuu intre scarile 0.5 si 2 +
-  - 0.05p - Desenati un alt cub pe care sa il deplasati prin spatiu la apăsarea tastelor W, A, S, D, E, Q (pozitiv și negativ pe toate cele 3 axe). +
-  - 0.05p - Desenati obiectele de 4 ori in 4 ferestre de afisare diferite, conform imaginii de mai jos. Pastrati proportiile precizate in imagine. +
-    * Utilizati in metoda ''​DrawObjects()''​ directiva de specificare a pozitiei si dimensiunii portii de afisare. +
-    * In metoda ''​Update()''​ desenati de 4 ori obiectele. Trimiteti la desenare cele 4 camere setate cu pozitii si directii de vizualizare predefinite. +
-{{ :​ppbg:​laboratoare:​result2.png?​700 |}}+
  
 +  - 0.1p - Completați fișierul ''​transform3D.h''​ cu transformările de modelare descrise mai sus.
 +  - 0.05p - Completați fișierul ''​transform3D.h''​ cu transformarea de proiecție perspectivă descrisă mai sus.
 +  - 0.05p - Aplicați procesul de împărțire perspectivă a coordonatei obținute în urma aplicării tuturor transformărilor din lanț.
 +  - 0.05p - Completați fișierul ''​transform3D.h''​ cu transformarea de vizualizare descrisă mai sus.
 +    * După acest pas, cât țineți apăsat butonul dreapta de la mouse, puteți modifica direcția de vizualizare a observatorului prin deplasarea poziției mouse-ului și vă puteți deplasa prin scena 3D cu tastele **W**, **A**, **S**, **D**, **E** și **Q**. Până în acest punct, rezultatul pe care ar trebui să îl obțineti este următorul: {{ :​ppbg:​laboratoare:​cube1.png?​600 |}}
 +  - 0.05p - Completați metoda ''​DetermineTriangleFace()''​ pentru a identifica corect care din cele două fațete ale triunghiului,​ față sau spate, este afișat.
 +    * După acest pas, puteți utiliza tasta **F** pentru a schimba între desenarea triunghiurilor pentru care se afișeaza fațetele față sau fațetele spate. Dacă alegeți opțiunea să fie afișate doar fațetele spate, ar trebui să puteți vedea interiorul cubului :) : {{ :​ppbg:​laboratoare:​cube2.png?​600 |}}
  
 +Bonus: Construiți și desenați un tetraedru care să aibă indicii fiecărui triunghiuri orientați în sens trigonometric,​ când triunghiul este privit din exterior. Asociați culori diferite fiecărui vârf pentru a vedea mai ușor detaliile tetraedrului.
  
ppbg/laboratoare/04.1698867033.txt.gz · Last modified: 2023/11/01 21:30 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