5. Geometrické transformace V této části předmětu 3D počítačová grafika se budeme zabývat geometrickými transformacemi 3D objektů. Jedná se o operace posuvů, otáčení, změny měřítka a zkosení těles vytvořených operacemi modelování. Stejnou problematikou pro 2D grafické objekty jsme se zabývali v předmětu Grafické editory. 5.1 Homogenní souřadnice Pro zjednodušení výpočtů transformací se s výhodou používá reprezentace bodů pomocí homogenních souřadnic. Tato reprezentace se používá z několika důvodů. Homogenní souřadnice umožňují vyjádření nejčastěji používaných lineárních transformací pomocí jediné matice, což v nehomogenních kartézských souřadnicích není možné. Další často používanou transformací vyjádřitelnou pomocí matic v homogenních souřadnicích je perspektivní promítání. Způsob vyjádření transformací pomocí matic je obzvlášť výhodný zejména proto, že pro jejich implementací lze využít existujících knihoven pro práci s maticemi. Skládání transformací se totiž v tom kontextu realizuje jako násobení matic, inverzní transformace je reprezentována inverzní maticí, atd. Moderní grafické procesory efektivně realizují výše uvedené maticové operace a rychlost zpracování scény se díky specializovaným grafickým kartám neustále zvyšuje. Uspořádaná čtveřice čísel [x, y, z, w] představuje homogenní souřadnice bodu P s kartézskými souřadnicemi [xh, yh, zh] ve třech rozměrech, jestliže platí
xh =
x , w
yh =
y z , zh = , w ≠ 0 w w
Bod P je svými homogenními souřadnicemi určen jednoznačně. Souřadnici w též nazýváme váhou bodu. Často se volí w = 1, potom jsou homogenní souřadnice bodu P [xh,yh,zh,1]. Obecnou matici 4 x 4 reprezentující lineární transformaci bodu P [x, y, z, w] na bod = [X, Y, Z, W] budeme označovat M, její speciální případy pak podle druhu transformace např. T (translace) ‚ R (rotace). Maticová rovnice transformace souřadnic bodů objektu má tvar x y 4× 4 ⋅ z w
M
X Y Z= W
Obsah matice M je potom závislý na typu transformace (otáčení, zkosení atd.).
5.2 Základní trojrozměrné transformace Pro lineární transformace v prostoru se v počítačové grafice se používají matice velikosti 4 x 4 s použitím homogenních souřadnic. Posunutí Posunutí ve 3D je určeno vektorem posunutí T = (Tx, Ty, Tz), který udává jednotlivá posunutí ve směrech souřadných os. Maticová rovnice pro posunutí T pro w = 1 X 1 Y 0 Z = 0 1 0
0 1 0 0
0 Tx x 0 Ty y 1 Tz z 0 1 1
Otáčení kolem pevného bodu ve směru souřadných os Pro otáčení objektu vůči pevnému bodu je třeba si uvědomit, těleso má v prostoru tři stupně volnosti. Musíme tedy specifikovat tento pevný bod, vektor který prochází pevným bodem ve směru kterého se objekt otáčí a úhel otočení (obr.5.1).
Obr. 5.1 3D otáčení kolem bodu Jedním z jednoduchých případů otáčení ve třech rozměrech je otáčení kolem středu tělesa pf umístěnému v počátku souřadného systému. Rotace ve směru konkrétní osy, například x, znamená, že se souřadnice x bodů rotujícího objektu nemění. Transformace reprezentující otáčení kolem osy x o úhel α 0 X 1 Y 0 cos α Z = 0 sin α 1 0 0
0 − sin α cos α 0
0 x 0 y 0 z 1 1
Obdobně jsou sestaveny transformace pro otáčení kolem os y a z. X cos α Y 0 Z = − sin α 1 0
0 sin α 1 0 0 cos α 0 0
0 x 0 y , 0 z 1 1
X cos α Y sin α Z = 0 1 0
− sin α
cos α 0 0
0 0 1 0
0 x 0 y 0 z 1 1
Pokud chceme body objektu otáčet kolem počátku souřadnic ve směru libovolného vektoru, vycházejícího z počátku, můžeme to provést postupným otáčením kolem jednotlivých os x, y, z. Předpokládejme postupné otáčení kolem z o úhel α, kolem y o úhel β a kolem x o úhel γ. Výsledná matice rotace bude R = Rx ⋅ Ry ⋅ Rz . Dalším případem je otáčení kolem bodu umístěného mimo počátek souřadného systému. (obr.5.2). V tomto případě použijeme transformaci složenou z posunutí T(pf ) do počátku souřadného systému, otočení R(α) o příslušný úhel a zpětného posunutí T(-pf ) do bodu pf .
Obr.5.2 Otáčení kolem bodu mimo počátek souřadného systému Například pro otáčení kolem osy z bude transformační matice M = T(p f )Rz ( α )T ( − p f )
Vynásobením transformačních matic T a Rz dostaneme složenou matici posuvu a otáčení
M
cos α sin α = 0 0
− sin α cos α 0 0
0 0 1 0
x f − x f cos α + y f sin α y f − x f sin α − y f cos α zf 1
Obecnou rotací nazýváme otáčení kolem bodu mimo počátek souřadného systému ve směru obecné osy v prostoru lze realizovat zase složením několika dílčích transformací kolem os x, y, z. Nalezení příslušné transformační matice však není jednoduché. Předpokládejme, že osa rotace je určena vektorem a = a x i + a y j + a z k , bod otáčení pf
(obr.5.3). Vektor z bodu pf do bodu transformovaného X je otočen vůči vektoru a z bodu pf do původního bodu x o úhel α.
Obr.5.3 Otáčení kolem obecné osy
Po odvození lze rotaci bodu X kolem bodu pf ve směru obecného vektoru a o úhel α popsat rotační maticí tvaru a x2 a a R(a , α ) = cos α ⋅ I + (1 - cosα ) ⋅ x y ax az 0
ax a y a
2 y
a y az 0
0 0 0 a + sin α ⋅ z −a 0 y 1 0
axaz a y az 2 z
a 0
− ax 0 ax 0
ay − ax 0 0
0 0 0 0
kde I je jednotková matice 4 x 4. Obecnou rotaci kolem bodu pf , který leží mimo střed tělesa řešíme podobně, jako u obecné rotace v rovině, tj. postupnými transformacemi. Zvolený bod otáčení pf posuneme do počátku souřadného systému, otočíme body transformovaného objektu o daný úhel a zpětným posunutím vrátíme výsledek do výchozí pozice. Transformační matice M bude složena ze tří základnici transformací M = T ( p f )R( a ,α )T ( − p f ) Změna měřítka Změnu měřítka S(sx,sy,sz) v prostoru popisují transformační maticová rovnice X sx Y 0 Z= 0 1 0
0 sy 0 0
0 0 sz 0
0 0 0 1
x y z 1
kde koeficienty s x ≠ 0, s y ≠ 0, s z ≠ 0 určují změnu ve směru příslušné souřadnicové osy. Pomocí měřítkových koeficientů můžeme realizovat některou z transformací souměrnosti (zrcadlení) v prostoru (středová souměrnost, souměrnost podle roviny a osová souměrnost). Například souměrnost podle roviny xy na obrázku je realizována pomocí koeficientů sx = 1, sy = 1, sz = -1. Zkosení Operaci zkosení ve třech rozměrech můžeme rozdělit na tři případy zkosení vůči jednotlivým rovinám s koeficienty zkosení Shyz, Shxz a Shxy.
Obr. 5.4 Zkosení vzhledem k rovině yz
Ve všech třech případech určují koeficienty shx, shy a shz míru zkosení v odpovídajícím směru. Matice transformace zkosení vůči rovině xy X Y Z = Shxy 1
x 1 y 0 ⋅ = z sh x 1 0
0 1 sh y 0
0 0 1 0
0 0 0 1
x y z 1
Transformační matice zkosení Shyz, Shxz 1 sh Shxz = x 0 0
0 0 1 shz 0 1 0 0
0 1 sh y 0 0 1 , Sh yz = 0 0 0 0 0 1
shz 0 1 0
0 0 0 1
5.3 Deformační trojrozměrné transformace Předchozí uvedené transformace zachovávají základní tvar objektu, protože jsou uniformní vůči souřadnicím. Pokud například použijeme při transformaci změny měřítka různé hodnoty sx, sy, sz v závislosti na některé souřadnici objekt se zužuje nebo rozšiřuje podél příslušné osy a tím se nedeformuje jeho základní tvar. Abychom deformovali základní tvar tělesa, musíme transformační koeficienty modifikovat nějakou funkční závislostí. Na obr. 5.5 jsou prezentovány deformační transformace zúžením, zkroucením a prohnutím. Transformace prohýbáním je provedena pro všechny tři souřadnice.
Obr. 5.5 Deformace zúžením, zkroucením a prohnutím Deformace zužováním Deformace zúžením se snadno realizuje pomocí operace změny měřítka, kdy se měřítkové koeficienty mění podle nějaké funkce. Vybereme souřadnou osu, podél které chceme provést zúžení tělesa a zvolíme funkci změny měřítka pro zbylé dvě souřadnice. Tato funkce může být pro každou souřadnici jiná.
Rovnice zúžení podél osy Z
X = f ( z) ⋅ x Y = f ( z) ⋅ y Z=z
Funkce f(z) může být lineární nebo nelineární. Dosazením f(z) do transformační matice změny měřítka dostaneme transformační matici zužování podél osy z. Deformace kroucením Deformace kroucením se realizuje pomocí operace otáčení, kdy se úhly natočení mění podle zvolené funkce. Kroucení podél osy Z
X = x ⋅ cos α − y ⋅ sin α Y = x ⋅ sin α + y ⋅ cos α Z=z kde α = f (z ) je funkce změny úhlu natočení.
Dosazením f(z) do transformační matice změny měřítka dostaneme transformační matici kroucení podél osy z. Deformovat kroucením lze také jen část tělesa v intervalu souřadnice z min ≤ z ≤ z max . Potom deformace kroucením podle osy z: X = x ⋅ cos f ( z ) − y ⋅ sin f ( z ) X =x
pro z < z min , z > z max
Y = x ⋅ sin f ( z ) + y ⋅ cos f ( z ) Y=y
pro z min ≤ z ≤ z max pro z min ≤ z ≤ z max
pro z < z min , z > z max
Z=z Deformace prohýbáním Deformace prohýbáním je založena na geometrické transformaci posuvu. Předpokládejme prohýbání podél osy Z. Předem se určí střed ohýbání z0, interval hodnot souřadnice z ve kterém se bude prohýbat a koeficient k lineárního prohnutí.
Deformační transformace lineárního ohýbání podél osy Z X = x + kz X =x Y = y + kz Y=y
pro z min ≤ z ≤ z max pro z < z min , z > z max pro z min ≤ z ≤ z max pro z < z min , z > z max
Z=z Dosazením f(z) = kz do transformační matice změny měřítka dostaneme transformační matici prohýbání podél osy z. Nelineární deformace nemohou být obecně aplikovány na objekty s povrchy tvořenými polygonálními sítěmi. Problémem je v takovém případě možné omezení spojitosti mezi vrcholy polygonů a dále polygonální rozlišení povrchu
5.4 Geometrické transformace v OpenGL V knihovně OpenGL existují tři transformační matice, které se postupně aplikují na body (vrcholy) popřípadě i na normály vrcholů. První transformační matice se jmenuje ModelView matrix. Na tuto matici se můžeme dívat jako na spojení modelové matice a pohledové matice, protože se používá jak pro nastavení pozice kamery, tak i pro manipulaci s objekty ve scéně. Druhá transformační matice se jmenuje Projection matrix a používá se pro nastavení perspektivní projekce kamery. Třetí transformační matice se jmenuje Viewport matrix a používá se po provedení perspektivní projekce k mapování objektů z abtraktních souřadnic do souřadnic okna. Tato poslední matice ve skutečnosti pouze provádí transformaci v dvojrozměrné ploše, proto se s ní v OpenGL nepracuje jako s "plnohodnotnou" maticí. Kromě těchto tří matic můžeme měnit matici, která se používá při mapování textur na povrch objektů. Tato matice se nazývá Texture matrix. Změna transformačních matic a nastavení aktuální transformační matice Při změně některé z transformačních matic musíme nejprve určit, kterou transformační matici budeme měnit. K tomuto účelu se používá funkce void glMatrixMode(GLenum mode). Tato funkce má jeden parametr mode, jímž určujeme matici, kterou budeme dalšími příkazy změnit. Parametr může nabývat tří hodnot, reprezentovaných symbolickými konstantami: GL_MODELVIEW - bude se měnit ModelView matrix, tj. matice, ve které jsou uloženy modelové a pohledové transformace (transformace objektů a nastavení kamery). GL_PROJECTION - bude se měnit Projection matrix, tj. matice, která se používá pro nastavení perspektivní nebo ortogonální projekce kamery. GL_TEXTURE - bude se měnit Texture matrix, tj. matice, která se používá při mapování textur na povrch objektů. Aktuálně nastavenou matici lze zjistit pomocí příkazu glGetIntegerv(GL_MATRIX_MODE, ¤tMode), po jehož provedení je v proměnné currentMode některá z konstant GL_MODELVIEW, GL_PROJECTION nebo GL_TEXTURE. Změna obsahu aktuálně nastavené transformační matice S obsahem aktuálně nastavené matice lze manipulovat pomocí funkcí glLoadIdentity(), glLoadMatrix*(), glMultMatrix*(), glTranslate*(), glScale*() a glRotate*(). Nejjednodušší z těchto funkcí je funkce void glLoadIdentity(void). Tato funkce nahraje do aktuálně nastavené transformační matice koeficienty odpovídající jednotkové matici, tj. matici, ve které jsou všechny prvky vynulovány s výjimkou prvků hlavní diagonály, které jsou nastaveny na jedničku. Tato matice hraje úlohu neutrálního prvku při násobení matic nebo při násobení vektoru maticí. Při nastavování některé transformační matice se v naprosté většině případů začíná touto funkcí, neboť pomocí ní matici "připravíme" na aplikaci dalších transformací.
Funkce glLoadMatrix*() se používá pro přímé nastavení prvků matice. Tato funkce existuje ve dvou variantách lišících se pouze typem pole, které funkci předáváme jako parametr. První varianta je nadeklarována jako void glLoadMatrixd(const GLdouble *m), druhá varianta jako void glLoadMatrixf(const GLfloat *m). U první varianty má pole jako své prvky hodnoty typu GLdouble, ve druhé variantě jsou to prvky typu GLfloat. Další funkce glMultMatrix*() slouží k vynásobení aktuálně nastavené transformační matice maticí zadanou jako parametr této funkce. Opět existují dvě varianty, které se liší
typem prvků předávaného pole. Tyto varianty jsou nadeklarovány jako void glMultMatrixd(const GLdouble *m) a void glMultMatrixf(const GLfloat *m). U první varianty se používají hodnoty typu GLdouble, u druhé typu GLfloat. Další tři funkce, glTranslate*(), glScale*() a glRotate*() jsou používány mnohem častěji než předchozí dvě funkce. U těchto funkcí se nemanipuluje přímo s jednotlivými prvky matice, ale zadávají se základní lineární transformace - posun, změna měřítka a rotace. Pro zadané transformace se vytvoří dočasná matice a aktuální matice (většinou je to matice ModelView) je touto dočasnou maticí vynásobena. Funkce glTranslate*(), která existuje ve dvou variantách void glTranslated(GLdouble x, GLdouble y, GLdouble z) a void glTranslatef(GLfloat x, GLfloat y, GLfloat z), specifikuje posun o vektor [x, y, z]. Funkce glScale*() s variantami void glScaled(GLdouble x, GLdouble y, GLdouble z) a void glScalef(GLfloat x, GLfloat y, GLfloat z) slouží k zadání transformace změny měřítka, kdy je těleso nezávisle zvětšeno/zmenšeno ve třech směrech odpovídajících jednotlivým souřadným osám. Funkce glRotate*(), která má opět dvě varianty void glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z) a void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z), specifikuje transformaci rotace. Těleso je otočeno o úhel angle okolo osy procházející počátkem a bodem (x, y, z). Úhel angle je v obou variantách zadán ve stupních. Bod, jímž osa rotace prochází, by neměl být nastaven na souřadnice (0, 0, 0). Získání hodnot prvků jednotlivých transformačních matic Pro získání hodnot, které mají jednotlivé prvky transformačních matic, lze použít funkci void glGetDoublev(GLenum pname, GLdouble * params) nebo void glGetFloatv(GLenum pname, GLfloat * params). Parametr pname musí obsahovat jednu z těchto symbolických konstant: GL_MODELVIEW_MATRIX, GL_PROJECTION_MATRIX nebo GL_TEXTURE_MATRIX. V poli params jsou potom vráceny prvky příslušné transformační matice.
Otevřeme prostředí Visual C++ a ve složce Prik9 projekt pr9.dsw. Sledujeme jednotlivé transformační příkazy funkce onDisplay, potom program spustíme a transformujeme objekt pomocí příslušnými klávesami.