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

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

 

Seriál OpenGL - 4. díl

Google       Google       23. 2. 2008       19 648×

Dnes se podíváme, jak pracovat s transformačními maticemi v praxi.

Reklama
Reklama

Začneme nejdříve tím, že si popíšeme a vysvětlíme, co všechno se dá v OGL s maticemi provádět. Něco už jsem zmiňoval minule, ale radši ještě jednou a popořádku.

Je vždy důležité mít jistotu, se kterou maticí právě manipuluji. Jak bylo řečeno předminule, OGL je stavový automat a to, která matice je právě aktivní pro úpravu, je také jeden ze stavů. Přepíná se pomocí funkce glMatrixMode(), která může mít následující hodnoty.

  • GL_MODELVIEW (defaultní stav)
  • GL_PROJECTION
  • GL_TEXTURE
  • GL_COLOR

Už není třeba ji tady více rozebírat, jelikož to, co bylo třeba, bylo řečeno minule. Stejně tak už jsem se minule zmínil o tom, jak nastavit matici na jednotkovou, tedy takovou, která „nic nedělá“. Je to pomocí funkce glLoadIdentity().

Ta nastaví matici na následující hodnoty.

1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

Co když ovšem nechceme nastavit matici jednotkovou, ale nějakou úplně jinou, a to ne tak, že bychom násobili stávající, ale prostě nahráli novou? Od toho je tu f-ce:

void glLoadMatrixd(const GLdouble * m);
void glLoadMatrixf(const GLfloat * m);

Ta má za parametr ukazatel na pole 4×4, a to buď pole float nebo double podle toho, jaký konkrétní tvar použijeme.

To se může hodit v případě, že máte nějakou matici transformací, kterou jste si připravili bokem. Pokud jste dobří v matematice, tak si ty matice můžete v zásadě složit sami, ale s tím já vám moc nepomůžu :). Jednodušší způsob je si tu matici transformací někde zvlášť vytvořit pomocí OGL funkcí a pak uložit do pole o 16 prvcích. K tomu potřebujeme funkci GlGet(). Ta má vždy 2 parametry; první říká, jakou informaci chceme z OGL vytáhnout, a druhý, jak ji chceme uložit. Dá se pomocí ní zjisti v zásadě úplně všechno, co člověk potřebuje. My se tady ale zabýváme pouze případem, kdy chceme zjistit hodnoty dané matice a uložit je. Funkce má opět více způsobů zápisu, a to podle konkrétní proměnné, do které ukládáme, pro nás budou relevantní tyto dva.

glGetDoublev(GLenum  pname, GLdouble * params);
glGetFloatv(GLenum  pname, GLfloat * params);

První parametr se liší podle toho, jakou matici chceme ukládat (tady tedy nezáleží na nastaveném stavu). Má hodnoty:

  • GL_MODELVIEW_MATRIX
  • GL_PROJECTION_MATRIX
  • GL_TEXTURE_MATRIX
  • GL_COLOR_MATRIX

Možná o něco užitečnější, než nahrávat celou novou matici, je násobit stávající (něco takového se v podstatě děje např. při volání f-ce glRotate), to zařizuje tato funkce:

void glMultMatrixd(const GLdouble * m);
void glMultMatrixf(const GLfloat * m);

Parametrem jí je předán ukazatel na 16 místní pole, v němž je uložena matice. Opět záleží na zápisu podle toho, jaký typ proměnné používáme. Tuto f-ci můžeme použít nejen pro vykonávání transformací, ale i třeba v případě, že si jen tak pro sebe potřebujeme vynásobit nějaké matice. Např. takto:

void nasobMatice() {
	glPushMatrix();
	glLoadMatrixf(matice1);
	glMultMatrixf(matice2);
	glGetFloatV(GL_MODELVIEW_MATRIX, vysledek);
	glPopMatrix();
}

Tedy, nejprve uložíme aktuální stav, abychom se k němu mohli vrátit později. Načteme první matici, vynásobíme ji druhou maticí, uložíme a vrátíme se k původnímu stavu.

Ještě je zde jedna užitečná funkce. Ta se používá k nastavení kamery. To se samozřejmě dá udělat rotací a transformací (nejde opět o nic jiného, než o vynásobení modelview matice (dále MV) nějakou novou maticí). F-ce gluLookAt() je ale docela jednoduchá na použití a intuitivní (bohužel ne pro všechny typy kamery).

void gluLookAt(	GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ);

F-ce přijímá za parametry jak proměnné typu double, tak i float. První tři jsou souřadnicemi bodu ve světě (tj. ve world space), ve kterém se kamera nachází, další tři určují bod (opět ve světě), na který se kamera dívá, a poslední tři pak definují vektor, který říká, kde je nahoře.

Podívejme se teď na trochu (dost) předělaný kód z minulé lekce. Přibude nám jedna funkce, která bude nahrávat a ukládat matice.

void vytvorMatice() {
     
     glPushMatrix();  //uloží se MV

     //kamera
     glLoadIdentity(); // vynuluje se
     gluLookAt(0.0,0.0,5.0,0.0,0.0,-10.0,0.0,0.1,0.0); // nastaví se kamera
     glGetFloatv(GL_MODELVIEW_MATRIX, MaticeKamera); // uloží se matice kamery
     
     glLoadIdentity(); 
     
     //krychle 1  
     if(fps>0.0) { //ošetříme dělení nulou
        uhel+=speed/fps;
     }
     glTranslatef(-1.5, 0.0, 0.0); //provedou se transformace pro první krychli
     glRotatef(uhel,1.0,1.0,1.0);
     glGetFloatv(GL_MODELVIEW_MATRIX, MaticeKrychle1); // uloží se matice
     
     glLoadIdentity(); // vynuluje se
     
     //krychle 2
     glTranslatef(0.0, 0.0,0.0); //provedou se transformace pro druhou krychli
     glRotatef(uhel,1.0,1.0,1.0);
     glGetFloatv(GL_MODELVIEW_MATRIX, MaticeKrychle2); // uloží se matice
     
     glLoadIdentity(); // vynuluje se
     
     //krychle 3
     glTranslatef(1.5, 0.0,0.0); //provedou se transformace pro třetí krychli
     glGetFloatv(GL_MODELVIEW_MATRIX, MaticeKrychle3);// uloží se matice
     
     glPopMatrix(); //vrátíme se zpět původní MV matici

}

Nejdříve uložíme daný stav modelview matice, abychom se k ní mohli na konci vrátit. Pak vynulujeme MV matici a nastavíme kameru pomocí funkce gluLookAt(). Říkáme jí, že kamera je umístěna v bodě (0,0,5), míří ve směru vektoru (0,0,-10). Výslednou matici kamery uložíme do pole MaticeKamera.

Pak opět vynulujeme (je to jako vygumovat si papír před tím, než začneme psát znova něco jiného). Nastavíme první krychli, uložíme do proměnné MaticeKrychle1. Zase vynulujeme a obdobně vytvoříme a uložíme matici pro druhou krychli. Ještě nám přibyla třetí, takže provedeme to samé i s ní.

Posléze se funkcí glPopMatrix() vrátíme k předchozímu stavu. Nezapomeňte všechny proměnné, se kterými se ve funkci VytvorMatice() pracuje, definovat ještě před ní.

Funkce onDisplay() se také změní.

void onDisplay(void){               
      timer(); //fce. měřící fps
      vytvorMatice();
	 
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
     glLoadIdentity();
         
     glMultMatrixf(MaticeKamera);
          
     glPushMatrix();
     glMultMatrixf(MaticeKrychle1);
     krychle(0.5);
     glPopMatrix();
     
     
     glPushMatrix();
     glMultMatrixf(MaticeKrychle2);
     krychle(0.5);
     glPopMatrix();
     
     glPushMatrix();
     glMultMatrixf(MaticeKrychle3);
     krychle(0.5);
     glPopMatrix();
        
     glutSwapBuffers();                          
} 

Na začátek nastavíme všechny matice. Posléze, místo toho, abychom prováděli transformace přímo na místě, pouze vynásobíme stávající matici uloženými předpřipravenými maticemi transformací. Opět zde také přibyla třetí krychle. S funkcí onDisplay() už teď v podstatě nemusíme nic dělat, jelikož všechny pohyby budeme řešit při tvoření transformačních matic.

Asi si říkáte „A co z toho…“. Na takto zjednodušeném příkladě to nevypadá moc užitečně. To je sice pravda, ale jakmile máte někde uložený pohyb nějakého objektu, tak ho pak už vždy můžete použít a nemusíte znovu a znovu provádět ty samé transformace. Uložená transformační matice je vlastně proměnná, ve které není jen nějaká číselná hodnota, ale pohyb. Lze s ní pak operovat stejně jako s jakoukoli jinou proměnou. Sčítat, odečítat, násobit, invertovat atd. Také se to hodí z jiných důvodů (jak už jsem se minule zmiňoval o přechodech mezi jednotlivými prostory), ale o tom až jindy, až se budeme zabývat světlem a stíny, případně nějakou základní fyzikou.

Další velice mocnou výhodou toho, že máme uložené jednotlivé matice, je, že můžeme tvořit vztahy a závislosti mezi jednotlivými pohyby. Podívejme se znovu na funkci vytvorMatice().

void vytvorMatice() {
     
     glPushMatrix();  //uloží se MV
     //kamera
     glLoadIdentity(); // vynuluje se
     gluLookAt(0.0,0.0,5.0,0.0,0.0,-10.0,0.0,0.1,0.0); // nastaví se kamera
     glGetFloatv(GL_MODELVIEW_MATRIX, MaticeKamera); // uloží se matice kamery
     
     glLoadIdentity(); 
     
     //krychle 1  
     glTranslatef(-1.5, 0.0, 0.0); //provedou se transformace pro první krychli
     glRotatef(uhel,1.0,1.0,1.0);
     glGetFloatv(GL_MODELVIEW_MATRIX, MaticeKrychle1); // uloží se matice
     
     glLoadIdentity(); // vynuluje se
     
     //krychle 2
     if(fps>0.0) { //ošetříme dělení nulou
        uhel+=speed/fps;
     }
     
     glMultMatrixf(MaticeKrychle1);
     
   	 glTranslatef(1.0, 0.0,0.0); //provedou se transformace pro druhou krychli
	 glRotatef(uhel,1.0,1.0,1.0);
     glGetFloatv(GL_MODELVIEW_MATRIX, MaticeKrychle2); // uloží se matice
     
     glLoadIdentity(); // vynuluje se
     
     //krychle 3
     
     glMultMatrixf(MaticeKrychle2);
     
   	 glTranslatef(1.0, 0.0,0.0); //provedou se transformace pro třetí krychli
     glGetFloatv(GL_MODELVIEW_MATRIX, MaticeKrychle3);// uloží se matice
     
     glPopMatrix(); //vrátíme se zpět původní MV matici

}

Nastavení kamery a první krychle zůstává stejné. Při nastavování druhé krychle nejdříve MV matici vynásobíme maticí té první. Co se stane? Pomyslným souřadným středem už pro ni (pro 2. krychli) není střed světa, ale střed první krychle (převedli jsme souřadný systém druhé krychle do souřadného systému první). Je to jako bychom seděli na krychli jedna, nevnímali, že se hýbe, a jenom bychom viděli druhou krychli visící kousek od ní (vzpomeňte si na vztažné soustavy ve fyzice). Tím, že se souřadný systém první krychle otáčí (vůči světu a tedy i pozorovateli) otáčí se podle něj pro nás i krychle 2. Pozor, posun pro druhou krychli se mění, jelikož ho už nenastavujeme vůči středu světa, ale vůči středu první krychle.

Aby nebylo komplikací málo, uděláme to samé se třetí krychlí. MV matici vynásobíme maticí druhé krychle. Její souřadný systém se tak stane podřazený systému druhé krychle (přebírá tak pohyby její i krychle 1).

Co s tím? Představte si, že děláte tank, ten se hýbe, zároveň se mu hýbe hlaveň a zároveň z hlavně vycházejí projektily. Kdybyste pohyby každého měli řešit zvlášť transformačními funkcemi, tak asi zešílíte :-).

Tak to by bylo protentokrát vše. Příště už se vrhneme na texturování a světla. Problém matic ale ještě zdaleka neopouštíme.

×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.

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ý