Seriál OpenGL - 3. díl
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama

Seriál OpenGL - 3. dílSeriál OpenGL - 3. díl

 

Seriál OpenGL - 3. díl

Google       Google       21. 12. 2007       18 410×

Jak jsem slíbil minule, dnes se budeme zabývat maticemi v OpenGL a také tím, jak fungují abstrakce prostoru.

Reklama
Reklama

Matice

Předpokládám, že většina z vás se již s pojmem matice někdy setkala. Pokud ne, tak vězte, že matice je něco jako dvourozměrné pole. S tím, že každý jeho prvek má nějaký význam. Například souřadnice bodu x,y,z tvoří matici o jednom sloupci a třech řadách. Každá hodnota zde specifikuje něco jiného a dohromady nám všechny dávají umístění bodu v prostoru.

Matice mají tu výhodu, že umožňují manipulovat s komplexnějšími systémy než jsou čísla, přičemž stále udržují vztahy mezi jednotlivými prvky. Například můžeme vzít bod x1,y1,z1 a přičíst k němu vektor x2,y2,z2. Výsledkem bude bod posunutý o daný vektor. Tedy bod v prostoru + vektor v prostoru = bod v prostoru. Výsledný bod je stále bod, tj. má souřadnice x,y,z, vztahy zůstaly zachovány, i když se změnila hodnota.

Stejně tak jako je možné maticí popsat bod, je možné jí v OGL popsat momentální transformace bodu (tj. rotace, posun a tak) najednou. Podobně je maticí popsán i způsob, jak se bod ve finále jeví na obrazovce, případně i způsob, jak jsou mapovány texturovací koordináty.

My se zatím budeme věnovat pouze tzv. modelview matici a projection matici (a spíše hlavně té první).

Pohyby v prostoru

Představte si, že máte model vytvořený v 3Ds Maxu a ten je složený z nějakých bodů majících dané koordináty. Nejjednodušší příklad je samozřejmě bod (berme ho jako model :-)). Vy ten model vezmete a vložíte do vaší aplikace. Jeho střed bude přesně tam, kde je kamera (vy), a bude se rozlézat na všechny strany okolo. Tj., pokud to bude jenom bod se souřadnicemi 0,0,-1, bude jednu jednotku před vámi. V tomto případě se souřadný systém bodu bude rovnat souřadnému systému obrazovky (respektive souřadnému systému světa, kde obrazovka, tedy kamera, leží v bodě 0,0,0 a dívá se podél osy z). Nebo ještě jinak, object-space toho bodu se bude rovnat world-space.

My ovšem chceme objekt posunout a otočit (vůči nám). Vynásobíme tedy souřadnice jeho bodů transformační maticí. Transformační matice je matice o 4 sloupcích a 4 řadách, v níž je uložen jak posun, tak rotace (můžeme si ji sestavit sami nebo použít OGL funkce, glRotate, glTranslate atd.). Je potřeba si to představit tak, že se samotnými souřadnicemi bodů se vlastně nic neděje, ty mají stále své hodnoty v object-space objektu. Co je posunuto, je počátek jejich souřadného systému ve world-space, ten byl předtím v bodě 0,0,0 a po změně se nachází např v 10,21,0 a osa x už nemíří doprava, ale nahoru. To ale všechno pouze relativně k world-space. Ještě se rozlišuje view space, ale o tom později.

Všechny tyhle prostory jsou jenom jakási abstrakce pomáhající v práci se scénou. To, co se vlastně doopravdy děje, je to, že do programu pošleme bod. Ten je vynásoben současnou modelview maticí (tedy maticí celkových transformací, které jsou potřeba s bodem udělat) a tak zobrazen v prostoru tam, kam patří. Je ale důležité vždy si to spíše uvědomovat v pojmech jednotlivých prostorů, jelikož se to v mnoha případech hodí. Představte si třeba, že potřebujete spočítat vzdálenost dvou objektů, nemůžete vzít souřadnice jejich středů, tak, jak je máte v Maxu, jelikož ty vždy mají smysl jenom v object-space toho daného objektu (a v zásadě jsou většinou 0,0,0). Potřebujete tedy jejich souřadnice vynásobit podle transformací, co s nimi byli provedeny a tím je vlastně matematicky převést do jednoho prostoru (nejlépe world-space) a pak teprve spočítat vzdálenost mezi výslednými hodnotami.

Pokud se podíváme na trochu upravený kód z minulého dílu, tak se tam děje toto…

void onDisplay(void){               
        timer(); //fce. měřící fps
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        glLoadIdentity();
     
        if (fps>0.0) { //ošetříme dělení nulou
            uhel+=speed/fps;
        }
     
       glTranslatef(1.0, 0.0, -4.0);
       krychle(0.5);
     
      glTranslatef(-2.0, 0.0, 0.0);
      glRotatef(uhel,1.0,1.0,1.0);
      krychle(0.5);
        
      glutSwapBuffers();                          
} 

Nejprve přijde funkce glLoadIdentity(), která vynuluje modelview (dále MV) matici. Nastaví ji na jednotkovou, tedy takovou, která při násobení bodu nemění jeho souřadnice. Následuje f-ce glTranslatef(). Ta vynásobí stávající MV matici maticí takovou, v níž je uložen posun o 1.0, 0.0, -4.0, tj. MV matice je nyní změněna a jakýkoli bod jí projde, bude podle ní přepočítán. To je případ první krychle, ta se vykresluje před námi posunuta o jednotku doprava od středu. Další f-ce, glTranslatef(), opět upraví stávající MV matici, a to tak, že vše, co přijde po ní, bude zase o kousek jinde. Všimněte si, že, aby byl objekt zobrazen o jednotku vlevo od středu, musíme provést posun o 2 jednotky. Jelikož nezadáváme posun od středu, ale od předchozího místa vykreslení. Následujíc glRotatef pak opět násobí danou matici, a to tak, aby docházelo k rotaci. Vše, co by bylo zobrazeno za ní, by se točilo také.

Takto hýbat s objekty po světě je docela nešikovné, jelikož by člověk musel pořád počítat s předchozí provedenou transformací. Jedním řešením je po každé transformaci opět nulovat MV matici. To by pak vypadalo takhle.

void onDisplay(void){               
	 timer(); //fce. měřící fps
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	 glLoadIdentity();
     
     if(fps>0.0) { //ošetříme dělení nulou
        uhel+=speed/fps;
     }
     
     glTranslatef(1.0, 0.0, -4.0);
     krychle(0.5);
     
     glLoadIdentity();
     
	 glTranslatef(-1.0, 0.0, -4.0);
	 glRotatef(uhel,1.0,1.0,1.0);
     krychle(0.5);
        
	 glutSwapBuffers();                          
} 

Všimněte si, že nyní má druhá funkce glTranslate v sobě obsažený i posun dozadu, jelikož MV byla celá vynulována a nemohli jsme využít toho, že posun dozadu byl již proveden.

Nejlepším způsobem, jak s tím vším pracovat, je ale ten následující, jelikož v sobě může zahrnout i něco jako kameru, tj. je proveden onen posun dozadu pro oddálení scény a pak se teprve řeší rozmístění jednotlivých objektů.

void onDisplay(void){               
	 timer(); //fce. měřící fps
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	 glLoadIdentity();
     
     if(fps>0.0) { //ošetříme dělení nulou
        uhel+=speed/fps;
     }
     
     
     glTranslatef(0.0, 0.0, -4.0);
          
     glPushMatrix();
     glTranslatef(1.0, 0.0, 0.0);
     krychle(0.5);
     glPopMatrix();
     
     
     glPushMatrix();
     glTranslatef(-1.0, 0.0,0.0);
     glRotatef(uhel,1.0,1.0,1.0);
     krychle(0.5);
     glPopMatrix();
        
     glutSwapBuffers();                          
} 

Přibyly zde dvě funkce, glPushMatrix() a glPopMatrix(). První z nich uloží momentální matici a druhá ji zase vyvolá zpět. Je to taková paměť matic na jedno použití. Stejně tak ale můžeme uložit matici, provést transformace a znovu uložit matici. glPopMatrix() pak vyvolává matice zpátky postupně v opačném pořadí. Tato dvojice funkcí pracuje s jakoukoli maticí, která je zrovna nastavená jako aktuální. Toto platí pro všechny ostatní funkce pracující s maticemi (pokud není specifikováno jinak a přímo), tedy i glTranslate, glRotate atd. To, s jakou maticí se zrovna chystáme pracovat, nastavuje funkce glMatrixMode().

My se zatím budeme zabývat pouze dvěma jejími hodnotami, a to GL_MODELVIEW a GL_PROJECTION.

Co se tedy v předcházejícím kódu děje? Nejprve se celá scéna posune dozadu (tedy kamera se posune dozadu). Tento stav se uloží a MV matice se vynásobí transformační maticí, která posune první kostku vpravo. Pak se vyvolá uložený stav a znovu se uloží. Druhá kostka se posune vlevo a otočí se s ní. Posléze se opět vyvolá základní MV matice. Pokud bychom pokračovali v přidávání objektů, předchozí transformace se jich nebudou týkat. Pouze onen první posun kamerou.

3 prostory

Teď se ještě znovu podíváme na poslední věc, kterou už jsem nakousl předtím, zatím moc k ničemu nebude, ale později se její znalost tisíckrát vyplatí. Už jsem se zmiňoval o čemsi jako object space (dále OS), world space (WS) a camera space (CS, někdy je také označován jako view space).

Hodnoty bodů v CS jsou vlastně ony vstupní hodnoty z 3Ds Maxu vynásobené celkovou MV maticí. WS je tím, co bychom označili jako svět. Tedy hodnoty bodů ve WS jsou hodnotami, jež jsou vynásobeny MW maticí, od které je odečtena matice kamery. To nám umožňuje pomyslně od sebe oddělit manipulaci s jednotlivými objekty, jejich rozmístění po světě atd. a pohyby kamerou, které po tom světě krouží, jak se jí zlíbí. V praxi se to většinou dělá tak, že na začátku framu nastavíme kameru a posléze všechny jednotlivé pohyby ve světě vydělujeme pomocí glPushMatrix a glPopMatrix (případně pomocí dalších funkcí, ale o nich až příště). Poslední, OS, jsem už zmínil, to jsou ty hodnoty z Maxu.

Tohle jsou tři základní prostory v OGL. Neexistují samozřejmě reálně, ale jsou to spíše matematické abstrakce, při kterých se pohybujeme na úrovni hodnot jednotlivých bodů. Je třeba si uvědomit, že jeden bod v prostoru se dá vztáhnout k ostatním na několika různých úrovních. Pokud hledáme vztahy dvou objektů, nemusíme je nutně oba převádět do WS, ale můžeme hodnoty jednoho z nich převést do OS toho druhého atd.

Závěr

Ještě nám zbývá projekční matice. Ta se nezabývá tím, jak se nám objekty umisťují po scéně, ale tím, jak je výsledná scéna promítnuta na monitor. Všechno, co bylo zmíněno předtím, platí i o ní, ale v praxi se s ní většinou moc nemanipuluje, prostě se na začátku nastaví a je to. Berte ji jako nastavení objektivu kamery, tedy v zásadě nastavení viditelného pole (šířka, výška, hloubka…).

To by bylo pro tentokrát vše. Možná vám to může připadat jako zbytečná teorie, když v zásadě posléze stejně posunu a otočím, co potřebuji, pomocí glRotate a glTranslate. Není to tak! Příště si to, co jsem právě popsal, ukážeme na praktických případech. Podíváme se na to, jaké jsou funkce pro práci s maticemi a jak se jich dá využít pro naši potřebu. Například pro tvorbu komplexnější scény s kamerou a více pohybujícími se objekty.

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

Tagy:
Hlasování bylo ukončeno    
0 hlasů
Google
Autor programuje v OpenGL, C++, PHP.
Web    

Nové články

Obrázek ke článku Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres HCPP16 pořádá od 30. září do 2. října nezisková organizace Paralelní Polis již potřetí, a to ve stejnojmenném bitcoinovém prostoru v pražských Holešovicích. Letos přiveze na třídenní konferenci přes 40 většinou zahraničních speakerů – lídrů z oblastí technologií, decentralizované ekonomiky, politických umění a aktivismu. Náměty jejich přednášek budou také hacking, kryptoměny, věda, svoboda nebo kryptoanarchie.

Reklama
Reklama
Obrázek ke článku ICT PRO školení zaměřené nejenom na ICT

ICT PRO školení zaměřené nejenom na ICT

Dovolte, abychom se představili. Jsme zaměstnanci společnosti ICT Pro, profesionálové v oblasti poskytování komplexních ICT služeb. Neboli služeb spojených s informačními a komunikačními technologiemi, které dnes - ve 21. století - tvoří  nedílnou součást běžného provozu všech moderních firem.

loadingtransparent (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })();
Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032016 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý