2D transformace

V této kapitole se zaměřím na transformace bodů ve 2D prostoru. Využiji k tomu znalostí z předchozí kapitoly o goniometrických funkcích a maticích. Uvidíte, k čemu se vám tyto znalosti budou prakticky hodit. Probírané 2D transformace budou: posun, otočení, zvětšení/zmenšení a zkosení. Pořád se nedostanu k žádnému programování, ale vězte, že všechny knihovny na „kreslení“ využívají tutéž matematiku (a to nejen v jazyku C/C++).

Matice

Pro popis transformací se v počítačové grafice využívají matice. Takzvané transformační matice (= matice, které se použijí pro transformaci). A to především kvůli vztahu, který říká, že násobením dvou transformačních matic získáte třetí transformační matici, která popisuje výsledek obou násobených transformací:

T'=AT

T označuje aktuální transformaci. Pro bod, který ještě není nijak transformován, to může být jednotková matice.
A je nějaká (další) matice transformace (posunu, otočení atd.). (Jak takové matice pro transformace vypadají popíšu dále.)
T' označuje výslednou matici transformace. Když touto maticí vynásobíte souřadnice bodu, dostanete souřadnice, na které se má bod posunout po aplikaci transformací T a A.

Toto skládání transformací ulehčuje a urychluje výpočty. Obvykle nechcete transformovat jen jeden bod, ale celou sadu bodů, která představuje nějaký obrázek. Třeba chcete pootočit (O), posunout (P) a zmenšit (Z) obrázek tanku. Vypočtete si transformační matici (T'), kterou složíte ze všech transformací, které chcete na tank aplikovat a tuto jednu matici (T') pak použijete pro transformaci každého bodu v obrázku:

T' = Z P OJ

Násobení matic není komutitavní, tj. záleží na pořadí násobení (zdůraznil jsem jej závorkami). Jak z příkladu vidíte, nová transformace, kterou chcete aplikovat na aktuální transformaci, se dává na levou stranu součinu.

Jak uvidíte na praktickém příkladu dále, je rozdíl, jestli obrázek nejdřív natočíte a pak posunete, nebo nejdřív posunete a pak natočíte.

První matice (J) je aktuální matice transformace. Pro bod v počátku souřadného systému můžete použít jednotkovou matici (jinak řečeno, nemusíte matici J do výpočtu vůbec zahrnovat). Pokud už je bod někam posunutý, můžete použít jeho transformační matici T' , kterou jste vypočítali dříve. To je jedna z těch geniálních věcí…

Při programování hry je běžné, že uživatel ovlivňuje polohu, otočení i velikost (přiblížení) nějakého objektu. Podle toho, jakou klávesu zrovna drží zmáčknutou, se objekt každých pár milisekund posouvá, otáčí a přibližuje. Díky skládání transformací je možné si všechny tyto pohyby, respektive výslednou pozici, zapamatovat jen v jedné (výsledné) matici.

Posun

První transformační matici kterou popíšu, je matice posunu. Vypadá takto:

1 0 Px 0 1 Py 0 0 1

Px je posun ve směru osy X, Py je posun ve směru osy Y.

Ikdyž se jedná o matici posunu ve 2D, má rozměry 3x3. A to proto, protože díky tomu budou výpočty fungovat tak, jak se nám to hodí – bude fungovat skládání transformací. Pochopíte z příkladu dále.

Podívejte se na obrázek níže. Vidíte na něm souřadnicový systém se směrem os, který se obvykle používá v grafických knihovnách. Cokoliv, co má nějakou souřadnici zápornou je mimo obrazovku. Viditelnou část obrazovky tvoří jen čtvrtý kvadrant (ten v pravo dole s modrým pozadím).

Na obrázku je 3x vykreslený tank. První (označený A), není nijak transformovaný. Grafické knihovny obvykle vykreslují obrázky tak, že levý horní roh obrázku je v počátku souřadného systému (přesně jako tank A).

Tank B je posunut o 200px po ose X i Y, tank C je pak posunut ještě o 200px po ose X a 150px po ose Y.

Matice pro tyto posuny vidíte hned za obrázkem.

posun

1) Posun

B = 1 0 200 0 1 200 0 0 1

2) Posun

C = 1 0 200 0 1 150 0 0 1

Matice pro tank "A" žádná není. Takto se vykreslí tank na obrazovku, pokud jeho pozici nijak netransformujete (nebo použijete jednotkovou matici).

Kumulace transformací

Z předchozího výkladu už to vlastně umíte, ale pro jistotu ještě ukáži výpočet kumulované matice, kterou můžete použít pro přesun obrázku z pozice "A" do pozice "C".

Pro pozici "B" se ještě nemá co kumulovat (nebo se násobí jednotkovu maticí, jak se to vezme):

1) T = B

1 0 200 0 1 200 0 0 1

Pro posun transitivita platí, takže můžete pořadí matic B a C přehodit, ale v zájmu dobrých mravů vypočítávám T tak, jak je dobrým zvykem, tj. druhá transformace bude na levé straně součinu:

2) T = C * B

1 0 200 0 1 150 0 0 1 * 1 0 200 0 1 200 0 0 1 = 1*1+0*0+200*0 1*0+0*1+200*0 1*200+0*200+200*1 0*1+1*0+150*0 0*0+1*1+150*0 0*200+1*200+150*1 0*1+0*0+1*0 0*0+0*1+1*0 0*200+0*200+1*1 = 1 0 400 0 1 350 0 0 1

Výsledek se dal očekávat. S maticemi 2x2 by vám to ale tak hezky nevyšlo.

Homogenní souřadnice

Teď už zbývá jen zodpovědět otázku, jak se pomocí matice vypočítá pozice, kam se má bod přesunout. A odpověď zní, vynásobením vektoru souřadnic bodu, jehož souřadnice chcete transformovat.

Jenže jste ve 2D prostoru a matice má rozměry 3x3. Proto musíte mít 3-rozměrné souřadnice. Proto se vymyslely tzv. homogenní souřadnice.

Homogenní souřadnice k souřadnicím [x,y] přidává ještě jednu souřadnici, obvykle označovanou w. Touto souřadnicí se vynásobí x a y, takže výsledná souřadnice vypadá tatko:

wx wy w

Za w se dosazuje nějaké nenulové číslo, ale obvykle se dosazuje číslo 1.

Řekněme, že chci transformovat bod [0,0]. Použiji tedy homogenní souřadnici [0,0,w]:

1 0 400 0 1 350 0 0 1 * 0 0 w = 1*0+0*0+400*w 0*0+1*0+350*w 0*0+0*0+1*w = 400w 350w w

Z homogenních souřadnic získáte 2D souřadnice tak, že první dvě souřadnice vydělíte tou třetí (w). To je, myslím, na první pohled jasné.

Ukáži ještě jeden příklad, tentokrát pro bod [50,50]:

1 0 400 0 1 350 0 0 1 * 50 50 1 = 1*50+0*50+400*1 0*50+1*50+350*1 0*50+0*50+1*1 = 450 400 1

Výsledkem je, že se bod z souřadnic [50,50] přesune na souřadnice [450,400]. Výsledku jsem se dobral pouze násobením kumulovanou tranformační maticí. (Já vím, už jsem s tou kumulací trochu otravnej).

Takovéto matematické výpočty za vás budou dělat grafické/matematické knihovny. Takže se vlastně nějakým násobením matic nemusíte vůbec zabývat. A ikdyž si budete chtít uchovávat různé matice transformací kvůli optimalizaci výpočtů, existuje dost open source matematických knihoven pro práci s maticemi. Takže se nemusíte namáhat s jejich programováním, natož se učit nějakou matiku. Důležité je ale rozumět tomu, co tyto výpočty znamenají.

Otočení

Popis dalších transformací už bude triviální. Stačí, když vám prozradím jak vypadá transformační matice a zbytek práce s touto maticí je úplně stejný, jako u posunu popsaného výše. Takže pro otočení vypadá matice takto:

cos(α) sin(α) 0 -sin(α) cos(α) 0 0 0 1

Na prvním příkladu otočení ukáži otočení a posun tanku. Protože se body otáčí kolem středu souřadného systému, po otočení o 90° zmizí tank z obrazovky. Posunutím o vektor (200,150)px se tank na obrazovku zase vrátí.

Mnoho začátečníků mívá ten problém, že chtějí obrázek jen pootočit, ale on se jim prapodivně pohybuje po obrazovce a občas z ní úplně zmizí. Vy už víte proč to tak je, takže vás to trápit nebude :-).

otoceni

1) Otočení

B = cos(pi/2) sin(pi/2) 0 -sin(pi/2) cos(pi/2) 0 0 0 1

2) Posun

C = 1 0 200 0 1 150 0 0 1

Jak otočit obrázek kolem svého středu? Musíte jej nejdřív přesunout tak, aby byl střed obrázku v počátku souřadného systému. Pak obrázek otočíte a po otočení jej posunete zpět na výsledné místo. Buď to můžete udělat takto pomocí 3 transformačních maticí (pro posun [-x,-y], pootočení α a posun [x,y], kde x,y jsou souřadnice středu obrázku a α úhel pootočení), nebo si můžete tyto tři transformační matice vynásobit (bez dosazování za proměnné x,y,α) a vytvořit tak transformační matici pro otáčení kolem středu.

Toto vám nechám za domácí úkol. V podstatě to není potřeba, protože grafické knihovny mají obvykle nějakou funkci rotate(α,x,y), která otočí obrázek o daný stupeň se středem otáčení [x,y]. Nebo mají funkci na posun souřadného systému. V takovém případě přesunete souřadný systém do středu obrázku, pootočíte ho a pak posunete souřadný systém zpět (nebo kam potřebujete).

Zpátky k příkladu. Řekněme, že chci pootočit bod ve středu tanku (bod [85,50]) o 90°:

cos(pi/2) sin(pi/2) 0 -sin(pi/2) cos(pi/2) 0 0 0 1 * 85 50 1 = 0 1 0 -1 0 0 0 0 1 * 85 50 1 = 0*85 + 1*50 + 0*1 -1*85 + 0*50 + 0*1 0*85 + 0*50 + 1*1 = 50 -85 1

Výsledek nevyšel tak, jak jsem znázornil na obrázku výše. Transformovaný bod se přesunul nahoru do prvního kvadrantu na souřadnici [50,-85] (místo do třetího kvadrantu, ve kterém je tank na obrázku). Bod se totiž otočil o 90° proti směru ručičkových hodinek. Aby se bod otočil jako na obrázku, máte dvě možnosti. Buď budete otáčet o -90°, nebo vyměníte znaménka u sínů v transformační matici. V grafických knihovnách, které budete používat pro vykreslování, bude transformační matice pro otočení dána, takže budete moci udělat jen tu první věc – vybrat si správné znaménko pro úhel otočení.

Problém vznikl tím, že výše popsaná transformační matice předpokládá, že osa y směřuje nahoru (jak je v matematice běžné) a ne dolu (jak je běžné v programování).

Další příklad demonstruje nekomutitavnost násobení matic. Jinak řečeno, závislost na pořadí. Oproti předchozímu příkladu se tank nejdříve přesune a až pak otočí.

otoceni

1) Posun

B = 1 0 200 0 1 150 0 0 1

1) Otočení

C = cos(pi/2) sin(pi/2) 0 -sin(pi/2) cos(pi/2) 0 0 0 1

Obrázek by mohl svádět k tomu, že střed otáčení tanku z B do C je ve středu tanku A, ale otáčí se dle počátku souřadného systému.

A to už je naposledy, co jsem vás a (ne)komutativnost upozornil. Slibuji :)

Odvození matice transformace pro otočení

Čistě pro zajímavost vám teď ukáži, jak se dá taková matice transformace pro otočení odvodit.

Na začátek se podívejte, jaké posunutí bodu vlastně matice transformace pro otočení popisuje:

cos(α) sin(α) 0 -sin(α) cos(α) 0 0 0 1 * x y 1 = cos(α)*x - sin(α)*y sin(α)*x + cos(α)*y 1

Potřebujeme se tedy dostat k těmto rovnicím:

x' = cosα *x - sinα *y y' = sinα *x + cosα *y

Při odvozování se soustředím na odvození x'. Odvození y' je zcela analogické, takže vám to nechám za domácí úkol.

Na obrázku níže je v modrém kolečku bod, jehož souřadnici [x,y] známe a který chceme posunout po kružnici o úhel α – do oranžového kolečka. Ze souřadnic [x,y] modrého bodu lze snadno vypočítat úhel β i poloměr kružnice r. Sečtením úhlů α a β získáme úhel, o který je posunutý výsledný bod. Se znalostí úhlu a poloměru pak snadno pomocí goniometrických funkcí dopočítáme polohu x' i y'.

Your Browser doesnt support SVG images.

K odvození budeme potřebovat několik vzorečků. Pythagorovu větu už jistě znáte. Poloměr r se spočítá takto:

r= x2+y2
Vzorec 1.

Úhel β se vypočítá třeba jako arcus cosínus cosínu, kde cosínus se počítá jako poměr x/r (jak už víte z předchozí kapitoly), nebo i pomocí arcus sínus:

β= arcos x r = arsin y r
Vzorec 2.

Z goniometrických funkcí také víte, že platí tento vzorec (x' je x-ová souřadnice posunutého bodu):

cos α+β = x' r
Vzorec 3.

Teď tuto rovnici vynásobím r a za β dosadím Vzorec 2:

x'= r * cos α+ arcos x r
Vzorec 4.

Ještě za poloměr r dosadím Vzorec 1:

x'= x2+y2 * cos α+ arcos x x2+y2

Heuréka! Máme vzoreček, který vypočítává novou souřadnici x' pouze v závislosti na úhlu posunutí α a souřadnicích posouvaného bodu [x,y].

Toto ale vůbec nepřipomíná vzorec, který jsem se snažil na začátku odvodit. Abych se k němu dostal, musím zavolat na pomoc Ptolemaiose, který přišel na součtové vzorce goniometrických funkcí. Jeden z nich zní takto:

cos α+β = cosα cosβ - sinα sinβ
Vzorec 5.

Teď se vrátím ke Vzorci 4 a použiji Vzorec 5 na rozložení cosínu:

x'= r * cosα * cos arcos x r - r* sinα * sin arcos x r

Dále víme, že cos(arcos(x)) = x:

x'= r * cosα * x r - r* sinα * sin arcos x r x'= cosα * x - r* sinα * sin arcos x r

Podobný trik by se hodil i pro druhou půlku vzorečku. Stačí použít Vzorec 2. a nahradit výpočet úhlu β pomocí arcus sínu:

x'= cosα * x - r* sinα * sin arsin y r x'= cosα * x - sinα * y

A je to! No nebyla to sranda? :-)

Zvětšování / zmenšování

Matice transformace pro změnu měřítka (scaling) vypadá takto:

Sx 0 0 0 Sy 0 0 0 1

Tato matice popisuje tyto transformace:

x'=Sx*x y'=Sy*y

Pokud je Sx větší než jedna, jedná se o zvětšování ve směru osy x, pokud je menší než 1, jedná se o zmenšování v ose x.

Podobně jako u otáčení je potřeba si dát pozor na to, že pokud nebudete mít střed obrázku v počátku souřadného systému, tak se touto transformací obrázek nejen zvětší, ale i někam posune.

otoceni

1) Posun

B = 1 0 50 0 1 50 0 0 1

2) Scale

C = 2 0 0 0 2 0 0 0 1

Stejně tak jako u otáčení i zde záleží na pořadí, v jakém transformace provedete. A stejně tak jako u otáčení, pokud chcete, aby se obrázek zvětšil, ale jeho střed se neposunul, můžete to udělat tak, že přesunete obrázek středem do počátku souřadné soustavy, pak obrázek zvětšíte (případně i otočíte) a zase jej posunete zpět. Nebo přesunete souřadný systém do středu obrázku.

Zkosení

Matice pro transforamaci zkosení (skew, shear) vypadá takto:

1 tanα 0 tanβ 1 0 0 0 1

Kde α je úhel zkosení dle souřadnice X a β je úhel zkosení dle souřadnice Y.

otoceni

1) Posun

B = 1 0 50 0 1 50 0 0 1

2) Zkosení o 45° podle souřadnice X

tan(45°) = 1

C = 1 1 0 0 1 0 0 0 1

Všimtěte si, že se tank C posunul o 50 px podél osy X. Platí zde to samé, co bylo řečeno u otáčení a zvětšování/zmenšování …

Závěr

V této kapitole jste se naučili základní 2D transformace. Víte, na jaké obtíže při transformacích můžete narazit i jak je řešit. Znalosti z této kapitoly se vám budou hodit v kapitole následující, která pojednává o malinko složitějších transformacích, transformacích v 3D prostoru.

Komentář Hlášení chyby
Created: 1.3.2016
Last updated: 19.11.2017
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..