Grafická knihovna OpenGL (6): využití transformačních matic

5. 8. 2003
Doba čtení: 6 minut

Sdílet

V dnešním dílu si ukážeme využití transformačních matic při skládání transformací. Také si popíšeme zásobník matic, který se využívá zejména při vykreslování složitých, hierarchicky uspořádaných trojrozměrných scén.

Použití transformačních matic, skládání transformací a zásobník matic

Jak jsme již napsali v minulém dílu, lze transformační matice použít k několika operacím:

  1. Změna polohy, velikosti a orientace těles – k tomuto účelu se používá ModelView matice, pomocí které lze specifikovat transformace translace (posuvu), rotace a změny měřítka.
  2. Změna polohy a natočení kamery – k tomuto účelu se taktéž používá ModelView matice, protože na změnu polohy kamery se lze dívat také jako na změnu polohy celé scény v opačném směru. Totéž platí pro rotace a změnu měřítka.
  3. Změna tělesa záběru – používá se Projection matice, pomocí které lze zadat tvar výřezu prostoru, který je kamerou snímaný. Používají se dvě tělesa: kvádr (ortogonální projekce) a komolý jehlan (perspektivní projekce). Více o nastavení kamery bude řečeno v dalších dílech seriálu.
  4. Změna transformace texturovacích souřadnic – používá se Texture matice, kde lze zadat transformaci prováděnou s texturovacími souřadnicemi. Více viz další díly věnované texturování.
  5. Změna okna výřezu, který se zobrazí na obrazovce. Nepoužívá se „plnohodnotná“ transformační matice, ale lze zadat tzv. Viewport, který specifikuje mapování ve 2D z abstraktních souřadnic do souřadnic okna.

Použití transformačních matic

Některé transformace se při vykreslování scény provádí pouze jednou. Typickou operací je nastavení tělesa záběru kamery pomocí projekční matice (Projection matrix) a výřezu pomocí změny Viewportu. Tyto operace se většinou provádí v callback funkci vyvolávané při změně velikosti okna – v demonstračních příkladech je to funkce void onResize(int w, int h). Další změny projekční matice a výřezu se většinou neprovádí. Výjimkou bývá současné zobrazení trojrozměrné scény a „dvojrozměrných textů“. V tomto případě se provádí změna projekční matice dvakrát; poprvé pro nastavení perspektivní projekce pro zobrazení trojrozměrné scény a podruhé pro nastavení ortogonální projekce pro vykreslení textů.

V době vykreslování je však velmi často měněna ModelView matice, protože s tělesy ve scéně je většinou nějakým způsobem manipulováno. Manipulace ponejvíce zahrnuje posun tělesa, změnu měřítka a rotaci. Tyto lineární transformace lze přímo zadat pomocí funkcí glTranslate*(),glSca­le() a glRotate() (viz předchozí díl tohoto seriálu). Před zadáváním samostatných transformací je však vždy zapotřebí nastavit transformační matici na jednotkovou zavoláním funkce glLoadIdentity(), jinak se transformace budou skládat, což v některých případech může způsobit chybnou funkci programu (jak vyplyne z dalšího textu, je skládání transformací v některých případech žádoucí).

Příklad na použití matice ModelView pro posun těles je demonstrován v příkladu číslo 7 (zde je i zdrojový kód s obarvenou syntaxí), kde jsou vykresleny čtyři trojúhelníky, z nichž každý je posunut o jiný vektor. Důsledkem použití transformační matice je, že se ve funkci void drawTriangle(GLflo­at red, GLfloat green, GLfloat blue) nemusí složitě počítat souřadnice trojúhelníků, neboť požadovaná transformace je už předem do transformační matice zadaná. Tento příklad je samozřejmě velmi jednoduchý, ale už z něj můžeme poznat, že například i složitou rotaci tělesa lze zadat pouze jedním příkazem a nemusí se programově přepočítávat pozice každého vertexu.

Skládání transformací

Pokud mezi zadáváním jednotlivých transformací nevkládáme příkaz glLoadIdentity(), dochází při modifikaci transformační matice k vynásobení původní hodnoty matice s dočasnou maticí, která je pro každou transformaci vytvořena. Důsledkem je, že výsledná transformační matice se chová tak, jakoby se transformace skládaly, tj. postupně prováděly. Jedná se praktickou vlastnost, protože sice můžeme zadat libovolné množství transformací, ale při výsledném transformování jednotlivých vrcholů (vertexů) se bude provádět pouze násobení čtyřsložkového vektoru jednou maticí velikosti 4×4. Protože však násobení matic není komutativní, musíme dát pozor na pořadí zadávání transformací.

V OpenGL platí, že transformace jsou na vertexy aplikovány v opačném pořadí, než jsou zavolány jejich korespondující příkazy. Jestliže například bude v programovém kódu sekvence příkazů glTranslatef(); glRotatef();, ve skutečnosti bude objekt (resp. jeho vertexy) nejprve otočen a teprve poté posunut. Na tuto vlastnost, která může při prvních pokusech s OpenGL působit problémy, se můžeme také dívat tak, že se neprovádí transformace se samotným objektem, ale postupně s celým souřadným systémem (nyní už ve správném pořadí).

Tato vlastnost OpenGL je demonstrována na příkladu číslo 8, který je k dispozici i ve formě zdrojového kódu s obarvenou syntaxí. Po spuštění se zobrazí čtyři trojúhelníky, na každý z nich je přitom aplikováno jiné pořadí transformací:

  1. Na bílý trojúhelník je aplikována pouze transformace rotace pomocí funkce glRotatef(). Trojúhelník je tedy zobrazen tak, že jeho levý vrchol je umístěn v počátku, což je levý dolní roh okna aplikace.
  2. Na zelený trojúhelník jsou aplikovány dvě transformace glRotatef()a glTranslatef(). Tyto transformace jsou však provedeny v opačném pořadí, tj. trojúhelník je nejprve posunut a teprve poté orotován okolo osy z – tj. počátku.
  3. Na červený trojúhelník je aplikována pouze transformace posunu glTranslatef(). Trojúhelník je vykreslen posunutý ze základní polohy bez jakékoliv rotace.
  4. Na modrý trojúhelník jsou aplikovány dvě transformace glTranslatef() a glRotatef(). Jelikož jsou transformace opět provedeny v opačném pořadí, je trojúhelník nejprve orotován okolo osy z(počátku) a teprve poté posunut dál od počátku. Výsledkem je, že trojúhelník zdánlivě nerotuje okolo počátku, ale okolo bodu, jehož souřadnice jsou zadány v příkazu glTranslatef().

Běh aplikace lze ovládat pomocí pěti kláves. Klávesa Esc ukončuje program a vrací řízení zpět operačnímu systému. Pomocí kláves , (čárka) a . (tečka) lze změnit úhel rotace trojúhelníků. Tuto rotaci lze změnit i pomocí kláves < a >, což jsou většinou stejné klávesy jako tečka a čárka, ale stisknuté spolu s modifikátorem­Shift.

Zásobník matic

Transformační matice lze v OpenGL ukládat do takzvaného zásobníku matic. Zásobník matic není nic jiného než datová oblast vyhrazená uvnitř bloku OpenGL, která se používá pro uložení šestnácti koeficientů transformační matice. Pro různé typy transformačních matic jsou vyhrazeny různé zásobníky s obecně odlišnou kapacitou. Aktuálně vybranou matici lze uložit na vrchol zásobníku zavoláním funkce void glPushMatrix(void), odstranění matice z vrcholu zásobníku obstará funkce void glPopMatrix(void). Ta matice, která je uložená na vrcholu zásobníku, je použita při transformacích.

Jak již bylo řečeno, je pro každý typ transformační matice (zejména ModelView a projekční matice) vyhrazen vlastní zásobník, který může mít odlišnou kapacitu. Většinou mívá zásobník pro ModelView matici největší kapacitu, protože se tato matice nejčastěji mění. Kapacitu jednotlivých zásobníků lze zjistit pomocí funkce void glGetIntegerv(GLe­num pname, GLint *params), kde za parametr pname můžete dosadit symbolické konstantyGL_MAX_MO­DELVIEW_STACK_DEP­TH resp. GL_MAX_PROJETI­ON_STACK_DEPTH. Pomocí této funkce je také možné zjistit aktuální obsazenost zásobníku – stačí použít parametry GL_MODELVIEW_STAC­K_DEPTH resp. GL_PROJECTION_STAC­K_DEPTH. Každá implementace OpenGL by měla mít zásobník pro ModelView matice s kapacitou minimálně třiceti dvou matic, pro zásobník projekčních a texturovacích matic je vyžadována pouze kapacita dvou matic.

Ukázka použití zásobníku matic je demonstrována na příkladu číslo 9, který je k dispozici i ve formě zdrojového kódu s obarvenou synaxí. Po spuštění je zobrazen jednoduchý stromeček, který se skládá z jednoho kmene a dvou větví. Stromečkem lze otáčet pomocí kláves . (tečka) a , (čárka). Lze také měnit úhel větvení; pro tento účel se používají klávesy >a < („většítko“ a „menšítko“).

ict ve školství 24

Při vykreslování stromečku se používá několik transformací. Vykreslování začíná na pozici [200, 200], proto je po provedení všech transformací nutné provést posun do tohoto bodu příkazem glTranslatef(200­.0f, 200.0f, 0.0f). Otáčení celého obrazce v ploše (tedy kolem roviny z) zajistí příkaz glRotatef(angle, 0.0f, 0.0f, 1.0f). Dále jsou použity transformace pro posun počátku vykreslování na konec kmene (příkaz glTranslatef(0.0f, 100.0f, 0.0f)) a vykreslení obou větví. Při této operaci použijeme zásobník matic – nejprve se souřadný systém natočí tak, aby se vykreslila první větev. Potom vyjmeme ze zásobníku původní stav souřadného systému a vykreslíme druhou větev.

V příští části seriálu si ukážeme vykreslování bitmap, tj. jednobarevných rastrových obrázků.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.