Differences

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

Link to this comparison view

ppbg:laboratoare:04 [2023/11/01 21:43]
andrei.lambru
— (current)
Line 1: Line 1:
-====== Laboratorul 04 ====== 
- 
-<note tip> 
-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. 
- 
-**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 tip> 
-Pentru rezolvarea cerințelor din cadrul acestui labroator: 
-  - [[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-ului, puteți să reconsultați [[:​ppbg:​setup-framework | pagina]] dedicată acestui lucru. 
-</​note>​ 
- 
-===== API-ul grafic OpenGL ===== 
- 
-==== Stergerea ecranului ==== 
- 
-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: 
- 
-<code cpp> 
-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: 
- 
-<code cpp> 
-glClearColor(0,​ 0, 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 ==== 
- 
-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]]. 
- 
- 
-=== Vertex Buffer Object (VBO) === 
- 
-Un vertex buffer object reprezintă un container în care stocăm date ce țin de conținutul vârfurilor precum: 
-  * 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]]**: ​ 
-<code cpp> 
-unsigned int VBO_ID; // ID-ul (nume sau referinta) buffer-ului ce va fi cerut de la GPU 
-glGenBuffers(1,​ &​VBO_ID);​ //​ se genereaza ID-ul (numele) bufferului 
-</​code>​ 
- 
-<​note>​ 
-Așa cum se poate vedea și din explicația API-ului, funcț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.\\ ​ 
-În exemplul de mai sus este generat doar 1 singur buffer iar ID-ul este salvat în variabila ''​VBO_ID''​. 
-</​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]]**:​ 
-<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]]**:​ 
- 
-<code cpp> 
-glBindBuffer(GL_ARRAY_BUFFER,​ VBO_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]]**:​ 
- 
-<code cpp> 
-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. 
-  * **GL_STATIC_DRAW** reprezintă un hint pentru driver-ul video în ceea ce privește metoda de utilizare a 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 a funcției [[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glBufferData.xhtml|glBufferData]] 
- 
- 
-<note tip> 
-Pentru a înțelege mai bine API-ul OpenGL vă recomandăm să citiți 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.\\ ​ 
-De asemenea, documentaț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>​ 
- 
-=== Index Buffer Object (IBO) === 
- 
-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. 
- 
-<code cpp> 
-glGenBuffers(1,​ &​IBO_ID);​ 
-glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,​ IBO_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. 
- 
-=== 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); 
-</​code>​ 
- 
-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>​ 
- 
-Î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. 
- 
-==== Optiunea de optimizare Face Culling==== 
- 
-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. ​ 
- 
-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): 
-<code cpp> 
-// mode can be GL_CW (clockwise) or GL_CCW (counterclockwise) 
-// the initial value is GL_CCW 
-void glFrontFace(GLenum mode​); 
-</​code>​ 
- 
-<​note>​ 
-Exemplu: pentru 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. 
-</​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]]:​ 
-<code cpp> 
-glEnable(GL_CULL_FACE);​ 
-</​code>​ 
- 
-Pentru a dezactiva face-culling se folosește comanda [[https://​www.opengl.org/​sdk/​docs/​man4/​html/​glEnable.xhtml|glDisable]]:​ 
-<code cpp> 
-glDisable(GL_CULL_FACE);​ 
-</​code>​ 
- 
-Pentru a specifica ce orientare a fețelor să fie ignorată se folosește comanda **[[https://​www.opengl.org/​wiki/​GLAPI/​glCullFace|glCullFace]]** 
-<code cpp> 
-// GL_FRONT, GL_BACK, and GL_FRONT_AND_BACK are accepted. 
-// The initial value is GL_BACK. 
-glCullFace(GL_BACK);​ 
-</​code>​ 
- 
-===== Aplicatiile grafice in timp real ===== 
- 
-Aplicatiile grafice in timp real realizeaza o desenare succesiva a cate unui cadru. Intre 2 cadre succesive, parametrii de desenare, precum pozitia si directia de vizualizare a 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. 
- 
-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 ==== 
- 
-In interiorul acestei bucle, in interiorul metodei ''​LoopUpdate''​ se realizeaza urmatorul proces pentru fiecare cadru: 
- 
-<code cpp> 
-// 1. Polls and buffers the events 
-window->​PollEvents();​ 
- 
-// 2. Computes frame deltaTime in seconds 
-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();​ 
-Update(static_cast<​float>​(deltaTime));​ 
-FrameEnd(); 
- 
-// 5. Swap front and back buffers - image will be displayed to the screen 
-window->​SwapBuffers();​ 
-</​code>​ 
- 
-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 ==== 
- 
-Interactiunea utilizatorului cu fereastra este realizata ​ 
- 
-Interactiunea utilizatorului cu tastele de la tastatura, in situatia in care fereastra este selectate. Aceasta interactiune poate fi de 3 feluri: 
-  - 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 curent, indiferent 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 mouse, interactiunea poate fi de 3 feluri, similar ca in situatia tastelor. 
- 
-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 cadru. De exemplu: 
- 
-<code cpp> 
- 
-void Lab4::​OnKeyPress(int key, int mods) 
-{ 
-    if (key == GLFW_KEY_R) { 
-        printf("​S-a apasat tasta R."); 
-    } 
-} 
-</​code>​ 
- 
-Metoda ''​OnKeyPress''​ se apeleaza pentru fiecare tasta apasata intr-un cadru. Trebuie verificat in interiorul metodei, pe baza parametrilor ce tasta a fost apasata. Pentru mai multe informatii despre fiecare metoda in parte, va rog sa cititi detaliile din [[https://​github.com/​UPB-Graphics/​gfx-framework-ppbg/​blob/​master/​src/​core/​window/​input_controller.h | fisierul header al clasei InputController]]. 
- 
-<note tip> 
-Apelul fiecarei metode se realizeaza in cadrul pasului 3 descris mai sus. 
-</​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 3 de mai sus, astfel ca se apeleaza inainte de metoda ''​Update''​. Recomandarea este sa utilizati aceasta metoda cand gestionati interactiunea cu utilizatorul. 
- 
-Suplimentar acestor metode specifice tratarii interactiunii cu utilizatorul,​ se poate folosi atributul ''​window'',​ intern clasei, pentru a verifica existenta anumitor evenimente: 
- 
-<code cpp> 
-if(window->​KeyHold(GLFW_KEY_R) { 
-    printf("​Tasta R este apasata."​);​ 
-} 
- 
-if (window->​MouseHold(GLFW_MOUSE_BUTTON_1)) { 
-    printf("​Butonul stanga de la mouse este apasat."​);​ 
-} 
-</​code>​ 
- 
-==== Animatii independente de numarul de cadre desenate pe secunda ==== 
- 
-Modificarea proprietatilor unui obiect intre 2 cadre succesive trebuie realizata pe baza timpului care a trecut intre cele 2 cadre, respectiv timpul trecut pentru desenarea cadrului anterior. Mai exact, in situatia in care dorim sa modificam pozitia unui obiect cu 5 unitati de spatiu pe secunda, de-alungul axei Z, in sens pozitiv, putem aplica urmatorul proces: 
- 
-<​code>​ 
-object_position.z += 5 * 0.016; 
-</​code>​ 
- 
-Valoarea 0.016 reprezinta timpul mediu de desenare a unui cadru la o frecventa de 60 de cadre pe secunda. Intr-o secunda, pozitia obiectului se va deplasa cu 5 unitati de-alungul axei Z, in sens pozitiv. Pentru a nu folosi direct valoarea aceasta, putem utiliza valoarea ''​deltaTime'',​ ce reprezinta timpul de desenare a cadrului precedent si este primita ca parametru in metodele ''​Update''​ si ''​OnInputUpdate''​. 
- 
-<​code>​ 
-object_position.z += 5 * deltaTime; 
-</​code>​ 
- 
-===== Cerinte laborator ===== 
- 
-<note important>​ 
-Pentru toate cerintele in care se precizeaza ca animatiile trebuie sa fie continue, utilizati valoarea ''​deltaTime''​. 
-</​note>​ 
- 
-  - 0.05p - Completați metoda ''​CreateMesh()''​ astfel încât să încărcați geometria in memoria RAM a procesorului grafic. 
-    * 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 |}} 
- 
- 
  
ppbg/laboratoare/04.1698867828.txt.gz · Last modified: 2023/11/01 21:43 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