This is an old revision of the document!


Laboratorul 03

Transformari 2D

Obiectele 2D sunt definite intr-un sistem de coordonate carteziene 2D, de exemplu, XOY, XOZ sau YOZ. In cadrul acestui laborator vom implementa diferite tipuri de transformari ce pot fi aplicate obiectelor definite in planul XOY: translatii, rotatii si scalari. Aceastea sunt definite in format matriceal, in coordonate omgene, asa cum ati invatat deja la curs. Matricile acestor transformari sunt urmatoarele:

Translatia

$$ \begin{bmatrix} {x}'\\ {y}'\\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & t_x\\ 0 & 1 & t_y\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} $$

Rotatia

Rotatia fata de origine

$$ \begin{bmatrix} {x}'\\ {y}'\\ 1 \end{bmatrix} = \begin{bmatrix} cos(u) & -sin(u) & 0\\ sin(u) & cos(u) & 0\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} $$

Rotatia fata de un punct oarecare

Rotatia relativa la un punct oarecare se rezolva in cel mai simplu mod prin:

  1. translatarea atat a punctului asupra carui se aplica rotatia cat si a punctului in jurul caruia se face rotatia a.i. cel din urma sa fie originea sistemului de coordonate.
  2. rotatia normala (in jurul originii),
  3. translatarea rezultatului a.i. punctul in jurul caruia s-a facut rotatia sa ajunga in pozitia sa initiala

Scalarea

Scalarea fata de origine

$$ \begin{bmatrix} {x}'\\ {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} $$

Daca $sx = sy$ atunci avem scalare uniforma, altfel avem scalare neuniforma.

Scalarea fata de un punct oarecare

Scalarea relativa la un punct oarecare se rezolva similar cu rotatia relativa la un punct oarecare.

Utilizarea bibliotecii GLM

In cadrul laboratorului folosim biblioteca GLM care este o biblioteca implementata cu matrici in forma coloana, exact acelasi format ca OpenGL. Forma coloana difera de forma linie prin ordinea de stocare a elementelor maricei in memorie, Matricea de translatie arata in modul urmator in memorie:

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 
 
}

Din aceasta cauza, este convenabil ca matricile sa fie scrise manual in forma aceasta:

glm::mat3 Translate(float tx, float ty)
{
	return glm::transpose(
		glm::mat3( 1, 0, tx, 
			   0, 1, ty, 
			   0, 0, 1)
	); 
}

In cadrul framework-ului de laborator, in fisierul Transform2D.h sunt definite functiile pentru calculul matricilor de translatie, rotatie si scalare. In momentul acesta toate functiile intorc matricea identitate. In cadrul laboratorului va trebui sa modificati codul pentru a calcula matricile respective.

Transformari compuse

De ce sunt necesare matricile? Pentru a reprezenta printr-o singura matrice de transformari o secventa de transformari elementare, in locul aplicarii unei secvente de transformari elementare pe un anume obiect.

Deci, daca dorim sa aplicam o rotatie, o scalare si o translatie pe un obiect, nu facem rotatia obiectului, scalarea obiectului urmata de translatia lui, ci calculam o matrice care reprezinta transformarea compusa (de rotatie, scalare si translatie), dupa care aplicam aceasta transformare compusa pe obiectul care se doreste a fi transformat.

Astfel, daca dorim sa aplicam o rotatie (cu matricea de rotatie $R$), urmata de o scalare ($S$), urmata de o translatie ($T$) pe un punct ($x$,$y$), punctul transformat (${x}'$,${y}'$) se va calcula astfel:

$$ \begin{bmatrix} {x}'\\ {y}'\\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & t_x\\ 0 & 1 & t_y\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} sx & 0 & 0\\ 0 & sy & 0\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} cos(u) & -sin(u) & 0\\ sin(u) & cos(u) & 0\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x\\ y\\ 1 \end{bmatrix} $$

Deci, matricea de transformari compuse $M$ este $M = T * S * R$.

In cadrul laboratorului, in fisierul Laborator3.cpp, exista o serie de obiecte (patrate) pentru care, in functia Update(), inainte de desenare, se definesc matricile de transformari. Comanda de desenare se da prin functia RenderMesh2D().

   modelMatrix = glm::mat3(1);
   modelMatrix *= Transform2D::Translate(150, 250);
   RenderMesh2D(meshes["square1"], shaders["VertexColor"], modelMatrix);

Pentru exemplul anterior, matricea de translatie creata va avea ca efect translatarea patratului curent cu (150, 250). Pentru efecte de animatie continua, pasii de translatie ar trebui sa se modifice in timp.

Exemplu:

tx += deltaTimeSeconds * 100;
ty += deltaTimeSeconds * 100;
model_matrix *= Transform2D::Translate(tx, ty);

Retineti: daca la animatie nu tineti cont de timpul de rulare al unui frame (deltaTimeSeconds), veti crea animatii dependente de platforma.

Exemplu: daca la fiecare frame cresteti pe tx cu un pas constant (ex: tx += 0.01), atunci animatia se va comporta diferit pe un calculator care merge mai repede fata de unul care merge mai incet. Pe un calculator care ruleaza la 50 FPS, obiectul se va deplasa 0.01 * 50 = 0.5 unitati in dreapta intr-o secunda. In schimb, pe un calculator mai incet, care ruleaza la 10 FPS, obiectul se va deplasa 0.01 * 10 = 0.1 unitati in dreapta intr-o secunda, deci animatia va fi de 5 ori mai lenta.

Din acest motiv este bine sa tineti cont de viteza de rulare a fiecarui calculator (data prin deltaTimeSeconds, care reprezinta timpul de rulare al frame-ului anterior) si sa modificati pasii de translatie, unghiurile de rotatie si factorii de scalare in functie de aceasta variabila.

Transformarea fereastra-poarta

Desenele reprezentate intr-un program de aplicatie grafica (2D sau 3D) sunt, de regula, raportate la un sistem de coordonate diferit de cel al suprafetei de afisare.

In exercitiile anterioare din acest laborator, coordonatele obiectelor au fost raportate la dimensiunea ferestrei definita prin glViewport().

Exemplu: Daca viewport-ul meu are coltul din stanga jos (0, 0) si are latimea 1280 si inaltimea 720, atunci toate obiectele ar trebui desenate in acest interval, daca vreau sa fie vizibile. Acest lucru ma conditioneaza sa imi gandesc toata scena in (0, 0) - (1280, 720). Daca vreau sa scap de aceasta limitare, pot sa imi gandesc scena intr-un spatiu logic (de exemplu imi creez toate obiectele in spatiul (-1, -1) - (1, 1), si apoi sa le desenez in poarta de afisare, dar aplicand ceea ce se numeste transformarea fereastra poarta.

In cele ce urmeaza vedem ce presupune aceasta transformare si cum pot sa imi gandesc scena fara sa fiu limitat de dimensiunea viewport-ului.

Definitia matematica:

$$ \frac{xp - xpmin}{xpmax - xpmin} = \frac{xf - xfmin}{xfmax - xfmin} $$ $$ \frac{yp - ypmin}{ypmax - ypmin} = \frac{yf - yfmin}{yfmax - yfmin} $$

Transformarea este definita prin 2 dreptunghiuri, in cele doua sisteme de coordonate, numite fereastra sau spatiul logic si poarta, sau spatiul de afisare. De aici numele de transformarea fereastra-poarta sau transformarea de vizualizare 2D.

F: un punct din fereastra

P: punctul in care se transforma F prin transformarea de vizualizare

Pozitia relativa a lui P in poarta de afisare trebuie sa fie aceeasi cu pozitia relativa a lui F in fereastra.

$$ sx = \frac{xpmax - xpmin}{xfmax - xfmin} $$

$$ sy = \frac{ypmax - ypmin}{yfmax - yfmin} $$

  • sx, sy depind de dimensiunile celor doua ferestre
  • tx, ty depind de pozitiile celor doua ferestre fata de originea sistemului de coordonate in care sunt definite.

$$ tx = xpmin - sx * xfmin $$ $$ ty = ypmin - sy * yfmin $$

In final, transformarea fereastra poarta are urmatoarele ecuatii:

$$ xp = xf * sx + tx $$ $$ yp = yf * sy + ty $$

Consideram o aceeasi orientare a axelor celor doua sisteme de coordonate. Daca acestea au orientari diferite (ca in prima imagine), trebuie aplicata o transformare suplimentara de corectie a coordonatei y.

Efectele transformarii

  • marire/micsorare, in functie de dimensiunile ferestrei si ale portii
  • deformare daca fereastra si poarta nu sunt dreptunghiuri asemenea
  • pentru scalare uniforma, $s=min(sx,sy)$, afisarea centrata in poarta presupune o translatie suplimentara pe axa Ox sau pe axa Oy:

$$Tsx = (xpmax - xpmin - s*(xfmax - xfmin)) / 2$$ $$Tsy = (ypmax - ypmin - s*(yfmax - yfmin)) / 2$$

  • decuparea primitivelor aflate in afara ferestrei vizuale

Matricea transformarii fereastra-poarta

De retinut este ca transformarea fereastra poarta presupune o scalare si o translatie. Ea are urmatoarea expresie, cu formulele de calcul pentru sx, sy, tx, ty prezentate anterior:

$$ \begin{bmatrix} xp\\ yp\\ 1 \end{bmatrix} = \begin{bmatrix} sx & 0 & tx\\ 0 & sy & ty\\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} xf\\ yf\\ 1 \end{bmatrix} $$

Transformarea de vizualizare este deja implementata in clasa Laborator3_Vis2D:

//2D vizualization matrix
glm::mat3 Laborator3_Vis2D::VisualizationTransf2D(const LogicSpace & logicSpace, const ViewportSpace & viewSpace)
{
	float sx, sy, tx, ty;
	sx = viewSpace.width / logicSpace.width;
	sy = viewSpace.height / logicSpace.height;
	tx = viewSpace.x - sx * logicSpace.x;
	ty = viewSpace.y - sy * logicSpace.y;
 
	return glm::transpose(glm::mat3(
		sx, 0.0f, tx,
		0.0f, sy, ty,
		0.0f, 0.0f, 1.0f));
}

In cadrul laboratorului, in clasa Laborator3_Vis2D, este creat un patrat, in spatiul logic (0,0) - (4,4). De retinut este faptul ca acum nu mai trebuie sa raportam coordonatele patratului la spatiul de vizualizare (cum se intampla in exercitiile anterioare), ci la spatiul logic pe care l-am definit noi.

logicSpace.x = 0;	// logic x
logicSpace.y = 0;	// logic y
logicSpace.width = 4;	// logic width
logicSpace.height = 4;	// logic height
 
glm::vec3 corner = glm::vec3(0.001, 0.001, 0);
length = 0.99f;
 
Mesh* square1 = Object2D::CreateSquare("square1", corner, length, glm::vec3(1, 0, 0));
AddMeshToList(square1);

In functia Update() se deseneaza acelasi patrat creat anterior, de 5 ori: patru patrate in cele patru colturi si un patrat in mijlocul spatiului logic. Se definesc 2 viewport-uri, ambele continand aceleasi obiecte. Primul viewport este definit in jumatatea din stanga a ferestrei de afisare, iar al doilea, in jumatatea din dreapta. Pentru primul viewport se defineste transformarea fereastra poarta default si pentru al doilea viewport, cea uniforma. Observati ca in al doilea viewport patratele raman intotdeauna patrate, pe cand in primul viewport se vad ca dreptunghiuri (adica sunt deformate), daca spatiul logic si spatiul de vizualizare nu sunt reprezentate prin dreptunghiuri asemenea.

Utilizare

Unde se poate folosi aceasta transformare fereastra poarta? De exemplu, intr-un joc 2D cu masini de curse, se doreste in dreapta-jos a ecranului vizualizarea masinii proprii, intr-un minimap. Acest lucru se face prin desenarea scenei de doua ori.

Daca de exemplu toata scena (traseul si toate masinile) este gandita in spatiul logic (-10,-10) - (10,10) (care are dimensiunea 20×20) si spatiul de afisare este (0,0) - (1280, 720), prima data se deseneaza toata scena cu parametrii functiei fereastra-poarta anterior mentionati:

LogicSpace logic_space = LogicSpace(-10, -10, 20, 20);
ViewportSpace view_space = ViewportSpace(0, 0, 1280, 720);
vis_matrix *= VisualizationTransf2D(logic_space, view_space);

Daca la un moment dat masina proprie este in spatiul (2,2) - (5,5), adica de dimensiune 3×3 si vreau sa creez un minimap in coltul din dreapta jos al ecranului de rezolutie 280×220, pot desena din nou aceeasi scena, dar cu urmatoarea transformare fereastra-poarta:

LogicSpace logic_space = LogicSpace(2, 2, 3, 3);
ViewportSpace view_space = ViewportSpace(1000, 500, 280, 220);
vis_matrix *= VisualizationTransf2D(logic_space, view_space);

Laboratorul 3

Descriere laborator

In cadrul acestui laborator aveti de programat in doua clase:

  • Laborator3.cpp, pentru familiarizarea cu transformarile 2D de translatie, rotatie si scalare
  • Laborator3_Vis2D.cpp, pentru familiarizarea cu transformarea fereastra-poarta

Din clasa Main puteti sa alegeti ce laborator rulati:

World *world = new Laborator3();

sau

World *world = new Laborator3_Vis2D();

OpenGL este un API 3D. Desenarea obiectelor 2D si aplicarea transformarilor 2D sunt simulate prin faptul ca facem abstractie de coordonata z.

Transformarea fereastra-poarta este si ea simulata pentru acest framework, dar veti invata pe parcurs ca ea este de fapt inclusa in lantul de transformari OpenGL si ca nu trebuie definita explicit.

Cerinte laborator

  1. Completati functiile de translatie, rotatie, scalare din /Laborator3/Transform2D.h
  2. Sa modifice pasii de translatie si rotatie si scalare pentru cele trei patrate ca sa creeze animatie.
  3. Cu tastele W, A, S, D sa translateze fereastra logica Laborator3_Vis2D (sa inteleaga ca atunci cand se duce fereastra in jos, pare ca obiectele scenei se duc in sus, cand se duce fereastra in stanga, obiectele se duc in dreapta, etc.). Cu tastele Z si X sa faca zoom in si zoom out pe fereastra logica (sa se inteleaga ca atunci cand micsorezi fereastra, pare ca se maresc obiectele si invers).
egc/laboratoare/03.1507893788.txt.gz · Last modified: 2017/10/13 14:23 by alexandru.gradinaru
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