This is an old revision of the document!
În cadrul acestei teme, va trebui să realizați un joc în care utilizatorul controlează un elicopter ce se deplasează pe suprafața unui asteroid deluros. Misiunea jucătorului este să culeagă mai multe mostre dintr-o resursă specială ce se găsește pe acest asteroid, ce are forma unui copăcel. Controlul jocului se realizează în totalitate prin apăsarea butonului dreapta de la mouse. Acest buton este utilizat pentru selecția poziției de destinație pentru deplasarea elicopterului și pentru selecția copacilor de pe asteroid.
Puteți viziona mai jos un filmuleț demonstrativ cu o aplicație construită pe baza framework-ului de laborator, care acoperă cerințele.
Procesul de desenare a suprafetei asteroidului se realizeaza in mai multi pasi:
Primul pas necesita realizarea unei geometrii suport sub forma unui plan creat dintr-o retea poligonala ce contine cel putin 10 000 de varfuri. Un exemplu pentru aceasta geometrie se gaseste in urmatoarea imagine:
Geometria suport se realizeaza pe CPU si se trimite la desenare pentru a se deforma conform urmatorilor pasi.
Al doilea pas realizeaza deformarea geometriei suport conform unei harti de inaltimi, similar precum in laboratorul 8. Rezultatul acestei operatii se poate observa in imaginea de mai jos:
Suprafata deformata la pasul anterior se coloreaza prin esantionarea a 2 texturi, pe baza inaltimii suprafetei, similar precum in bonusul din laboratorul 8. Rezultatul se poate vedea in urmatoarea imagine:
Ultimul pas este descris in sectiunea urmatoare si reprezinta deformarea suprafetei pentru a se obtine o curbura. Se poate observa rezultatul in imaginea de mai jos:
Rezultatul este doar un efect vizual ce curbează geometria în jurul elicopterului, astfel că acesta poate ajunge la limita geometriei lumii, altfel spus la capătul ei și să și depășească acest capăt :) .
Realizeaza curburii obiectelor din lume se realizează la pasul de desenare a obiectelor din scenă. Acest efect se creează prin modificarea componentei y a coordonatelor pentru toate vârfurile din care este realizata suprafata asteroidului. Procesul este creat în vertex shader. Componenta y a tuturor vârfurilor se modifică după cum urmează:
$$ Pozitie_{v_y} = Pozitie_{v_y} - \|{Pozitie_{elicopter}-Pozitie_v}\|^2 \cdot factorCurbura $$
Factorul de curbură este proporțional cu dimensiunea obiectelor din lume. Pentru demo-ul de mai sus, este utilizat un factor de 0.02.
Pentru plasarea unui obiect pe suprafata asteroidului trebuie sa realizam urmatoarele transformari:
Pentru realizarea transformarilor de mai sus, aveti 2 optiuni:
Geometria elicopterului poate fi vizualizata in animatia de mai jos.
Geometria elicopterului este compusa din doua cuburi redimensionate neuniform pentru a defini cabina si coada elicopterului, impreuna cu 4 cuburi, de asemenea redimensionate neuniform pentru a reprezenta cele 2 elice. Geometria cabinei si a cozii are o culoare diferita de cea a elicelor.
Dupa cum se poate vedea si in imaginea de mai sus, cele 2 elice au o animatie continua de rotatie.
Geometria marcajului destinatie pentru elicopter poate fi vizualizata in imaginea de mai jos.
Paralelipipedul din partea superioara este obtinut prin redimensionarea neuniforma a unui cub. Acesta are o animatie continua de oscilatie in directia sus-jos.
In partea de jos a marcajului, se afla un disc de cerc ce se afla putin de-asupra terenului.
Geometria copacului se realizeaza prin desenarea unei ierarhii de paralelipipede cu mai multe niveluri, unde fiecare la fiecare nivel, se deseneaza un paralelipiped redimensionat uniform, rotit si translatat in capatul paralelipipedului de la nivelul anterior. Pentru a finaliza desenarea, se aplica la final toate transformarile utilizate in desenarea paralelipipedului de la nivelul anterior. Rezultatul se poate vedea in imaginea de mai jos.
La fiecare nivel al ierarhiei de paralelipipede, se deseneaza 3 ramuri de ierarhie, fiecare rotita la 120 de grade fata de axa OY, una fata de cealalta. Pentru copacul din imagine, s-au utilizat 6 niveluri ale ierarhiei de paralelipipede.
Pentru colorarea copacului, se utilizeaza procesul de mapare cilindrica. Se folosesc 2 texturi, una pentru trunchiul copacului si una pentru frunzisul coroanei. Pentru a calcula coordonatele de textura in varfurile geometriei ce descrie copacul, se utilizeaza procesul de mapare cilindrica ce are urmatoarele formule:
$$ u = \frac{1}{2\pi}arctan(z/x) \\ v = \frac{y}{treeHeight} $$
unde u si v sunt componentele x si y ale coordonatei de textura si (x, y, z) reprezinta componentele coordonatei varfului in spatiul lumii.
Suplimentar, pentru imbunatatirea calitatii vizuale, desenarea se realizeaza dupa cum urmeaza:
Rezultatul poate fi vizualizat in imaginea de mai jos.
Pentru a realiza procesul de selectie, se deseneaza toata scena intr-un obiect de tip framebuffer creat de voi. Acest framebuffer contine 2 texturi de culoare:
RGBA32F
si tipul de data float
.
Pentru a afisa informatia din prima textura de culoare in fereastra, trebuie copiat continutul primei texturi de culoare din obiectul de tip framebuffer creat de voi in textura de culoare a obiectului de tip framebuffer implicit, ce apartine ferestrei. Pentru acest proces, puteti utiliza urmatorul cod:
// custom_framebuffer_object reprezinta identificatorul obiectului de tip framebuffer creat de voi glBindFramebuffer (GL_READ_FRAMEBUFFER, custom_framebuffer_object); glReadBuffer (GL_COLOR_ATTACHMENT0); glBindFramebuffer (GL_DRAW_FRAMEBUFFER, 0); glBlitFramebuffer (0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
Texturile de culoare din interiorul obiectului de tip framebuffer creat de voi trebuie sa aiba aceeasi dimensiune precum textura de culoare din interiorul obiectului de tip framebuffer implicit, ce apartine ferestrei. Acest cod trebuie utilizat la finalul cadrului.
Pentru a realiza procesul de selectie a destinatiei, in canalele de culoarea RGB, ale celei de-a doua texturi de culoare ale obiectului de tip framebuffer creat de voi, pastrati coordonata (x, 0, z) a geometriei suport, dupa pasul de deformare pe baza hartii de inaltime si inainte de deformarea pe baza curburii. Cele 2 texturi de culoare pot fi vazute in imaginea de mai jos:
Rezultatul vizual din imagine este obtinut dupa aplicarea deformarii pe baza curburii, dar coordonata (x, 0, z) pastrata in cea de-a doua textura de culoare este cea calculata inainte de realizarea curburii.
Pentru a extrage coordonata (x, 0, z) din cea de-a doua textura de culoare din obiectul de tip framebuffer creat de voi, la momentul apasarii butonului dreapta de la mouse de catre utilizator, extrageti informatia din pixelul la care se afla cursorul la momentul apasarii butonului. Codul pentru acest proces arata in felul urmator:
// x si y reprezinta pozitia mouse-ului pe ecran, in pixeli. float data [4]; y = window->props.resolution.y - y; // custom_framebuffer_object reprezinta identificatorul obiectului de tip framebuffer creat de voi glBindFramebuffer (GL_FRAMEBUFFER, framebuffer_object); glReadBuffer (GL_COLOR_ATTACHMENT1); glReadPixels (x, y, 1, 1, GL_RGBA, GL_FLOAT, data);
Pentru a realiza procesul de selectie a unui obiect, se pastreaza suplimentar in canalul A din cea de-a doua textura de culoare a obiectului de tip framebuffer creat de voi un identificator pentru toate obiectele din scena. O prezentare vizuala a acestor identificatori, sub forma de nuante de gri, se poate vedea in imaginea de mai jos.
Nuantele de gri din cea de-a doua textura de culoare din imaginea de mai sus au scop de prezentare pentru a se putea vizualiza identificatorul sub forma unei culori. Datorita faptului ca cea de-a doua textura de culoare contine informatie de tip RGBA32F
, aceasta permite pastrarea unor valori ce depasesc valoarea 1.
Elicopterul se deplaseaza spre locatia de destinatie in momentul in care utilizatorul alege o astfel de destinatie prin apasarea butonului dreapta de la mouse. La momentul alegerii destinatiei, se plaseaza pe suprafata obiectul de marcaj prezentat mai sus si acesta ramane la pozitia respectiva pana la momentul in care elicopterul ajunge la destinatie. In momentul in care elicopterul se opreste, obiectul de marcaj al destinatiei dispare.
Atasat de elicopter se afla o camera observator ce urmareste elicopterul. Suplimentar, in momentul deplasarii elicopterului, acesta este orientat cu fata spre directia de deplasare.
În situația în care se utilizează framework-ul de laborator, pentru desenarea geometriei din perspectiva camerei observator, se poate utiliza obiectul de tip Camera
, după cum urmeaza:
auto camera = GetSceneCamera(); // pozitia relativa a camerei fata de pozitia personajului glm::vec3 relativeCameraPosition = ...; // playerPosition este pozitia in lume a personajului controlat de jucator camera->SetPositionAndRotation( playerPosition + relativeCameraPosition, glm::quatLookAt(-glm::normalize(relativeCameraPosition), glm::vec3(0, 1, 0)) );
Pentru a orienta geometria personajului în direcția de deplasare, se utilizează o matrice de rotație în jurul axei OY. În situația în care se cunoaște direcția de deplasare, unghiul de rotație se poate calcula după cum urmează:
$$ unghi = arctan(\frac{directie_x}{directie_z}) $$
atan2()
pentru calcularea arctangentei.
plane50.obj
. Geometria suport trebuie generata din cod si trebuie sa aiba cel putin 10000 de varfuri.