Zajímali jste se někdy o to, jak fungují OpenGL funkce pracující s maticemi? V tomto článku si vysvětlíme, jak fungují funkce typu glTranslatef(), glRotatef(), glScalef() a jak je případně nahradit vlastním kódem.
Matice jsou to, co dělá naši aplikaci 3D, bez nich by rotace a posuny byly prakticky nemožné. Každý vrchol ve scéně bychom museli zadávat ručně - strašná představa pro každého programátora. Ve 3D mají standardní matice velikost 4x4.
[1, 0, 0, 0]
[0, 1, 0, 0]
[0, 0, 1, 0]
[0, 0, 0, 1]
Pokud bychom vynásobili bod maticí nahoře (ekvivalent násobení jednou), žádná změna by se nekonala. Proto tuto matici nazýváme maticí identity, je základem pro všechny ostatní. Samozřejmě, že chceme provádět nejrůznější translace a rotace, pro ně máme další matice.
Čísla tx, ty a tz představují hodnoty posunu.
[ 1, 0, 0, 0]
[ 0, 1, 0, 0]
[ 0, 0, 1, 0]
[tx,ty,tz, 1]
Rotace na ose x:
[ 1, 0, 0, 0]
[ 0, cos(xrot),-sin(xrot), 0]
[ 0, sin(xrot), cos(xrot), 0]
[ 0, 0, 0, 1]
Rotace na ose y:
[ cos(yrot), 0, sin(yrot), 0]
[ 0, 1, 0, 0]
[-sin(yrot), 0, cos(yrot), 0]
[ 0, 0, 0, 1]
Rotace na ose z:
[ cos(zrot),-sin(zrot), 0, 0]
[ sin(zrot), cos(zrot), 0, 0]
[ 0, 0, 1, 0]
[ 0, 0, 0, 1]
Čísla sx, sy a sz představují úroveň zmenšení/zvětšení.
[sx, 0, 0, 0]
[ 0,sy, 0, 0]
[ 0, 0,sz, 0]
[ 0, 0, 0, 1]
Matice můžeme mezi sebou kombinovat také tak, že je vynásobíme. Namísto používání funkcí glTranslatef(), glRotatef() a glScalef() můžeme libovolný bod transformovat prostřednictvím některé z těchto matic. Všimněte si, že posunem změníme matici i pro jakoukoli další operaci - úpravy nejsou dočasné, ale trvalé.
Anglický originál článku můžete najít na adrese http://nehe.gamedev.net/data/articles/article.asp?article=02.
Pomocí uvedených výpočtů se můžeme vyhnout používání příkazů glRotatef() a glTranslatef(). Modelovou matici si můžeme spočítat sami a pak ji pouze předat OpenGL pomocí příkazu glLoadMatrix() - přesně takto to dělá například hra Quake III Arena.
Z modelové matice můžete libovolně číst i libovolně do ní zapisovat. Zápis se uskutečňuje pomocí funkce glLoadMatrix(), čtení za pomoci funkce glGetDoublev(GL_MODELVIEW_MATRIX, <pole do kterého se mají data načíst>);
Následují funkce demonstrují používání matic 4x4. Jak už plyne z názvu, funkce Load_Identity() resetuje matici uloženou v globálním poli m.
void Load_Identity(void)// Reset matice
{
int i;
for(i = 0; i < 16; i++)
{
m[i] = 0.0f;
}
m[0] = 1.0f;
m[5] = 1.0f;
m[10] = 1.0f;
m[15] = 1.0f;
}
Někdy můžeme chtít použít matici 3x3 (obsahuje pouze informace o rotaci). Jak vidíte, získání z matice 4x4 je jednoduché. Stačí ji ořezat o poslední řádek a sloupec.
void MATRIX3x3Convert_From_MATRIX4x4(MATRIX4x4 m1)// Ořezání matice 4x4 na 3x3
{
m[0] = m1.m[0];
m[1] = m1.m[1];
m[2] = m1.m[2];
m[3] = m1.m[4];
m[4] = m1.m[5];
m[5] = m1.m[6];
m[6] = m1.m[8];
m[7] = m1.m[9];
m[8] = m1.m[10];
}
Následující dvě funkce přebírají aktuální matici a s její pomocí násobí bod nebo vektor. Získáme tak jeho absolutní (skutečnou) pozici v prostoru. Argumenty by měly být jasné - matice a vektor. Bez této funkce by prakticky nešly počítat kolize objektů ve scéně.
VECTOR Matrix_krat_vektor(MATRIX4x4 m1, VECTOR v1)// Násobení vektoru maticí 4x4
{
VECTOR temp;
temp.vx = v1.vx * m1.m[0] + v1.vy * m1.m[4] + v1.vz * m1.m[8] + m1.m[12];
temp.vy = v1.vx * m1.m[1] + v1.vy * m1.m[5] + v1.vz * m1.m[9] + m1.m[13];
temp.vz = v1.vx * m1.m[2] + v1.vy * m1.m[6] + v1.vz * m1.m[10] + m1.m[14];
return temp;
}
VERTEX3D Matrix_krat_bod(MATRIX4x4 m1, VERTEX3D p1)// Násobení bodu maticí 4x4
{
VERTEX3D temp;
temp.x = (float)(p1.x * m1.m[0] + p1.y * m1.m[4] + p1.z * m1.m[8] + m1.m[12]);
temp.y = (float)(p1.x * m1.m[1] + p1.y * m1.m[5] + p1.z * m1.m[9] + m1.m[13]);
temp.z = (float)(p1.x * m1.m[2] + p1.y * m1.m[6] + p1.z * m1.m[10] + m1.m[14]);
return temp;
}
Teď ještě jednou to samé ale s použitím matice 3x3.
VECTOR Matrix_krat_vector(MATRIX3x3 m1, VECTOR v1)// Násobení bodu maticí 3x3
{
VECTOR temp;
temp.vx = v1.vx * m1.m[0] + v1.vy * m1.m[3] + v1.vz * m1.m[6];
temp.vy = v1.vx * m1.m[1] + v1.vy * m1.m[4] + v1.vz * m1.m[7];
temp.vz = v1.vx * m1.m[2] + v1.vy * m1.m[5] + v1.vz * m1.m[8];
return temp;
}
VERTEX3D Matrix_krat_bod(MATRIX3x3 m1, VERTEX3D p1)// Násobení bodu maticí 3x3
{
VERTEX3D temp;
temp.x = (float)(p1.x * m1.m[0] + p1.y * m1.m[3] + p1.z * m1.m[6]);
temp.y = (float)(p1.x * m1.m[1] + p1.y * m1.m[4] + p1.z * m1.m[7]);
temp.z = (float)(p1.x * m1.m[2] + p1.y * m1.m[5] + p1.z * m1.m[8]);
return temp;
}
Výpočty s maticemi mají mnohem více možností, např. jsem vůbec neuvedl, jak spočítat v matici posun a rotaci nebo jak výsledek předat OpenGL. Tím bychom se mohli kompletně vyhnout používání funkcí glRotatef() a glTranslatef(). Jak už jsem napsal, matice se hlavě používají k získání absolutní pozice bodu ve scéně, kterou potřebujeme při výpočtech kolizí, na všechno ostatní se používají standardní OpenGL funkce. Další podrobnosti můžete najít např. v NeHe Tutoriálu 30 (třída Tmatrix v souborech Tmatrix.cpp a Tmatrix.h). Především z tohoto zdroje jsem čerpal.
Přiložený zdrojový kód demonstruje, jak získat skutečnou pozici bodu v prostoru za použití modelové matice. Po volání funkcí glTranslatef() a glRotatef() vykreslíme krychli a spočítáme pozice jejích vrcholů, které následně zobrazíme na scénu vpravo nahoře.
napsal: Paul Frazee - The Rainmaker <frazee (zavináč) swbell.net>
přeložil: Přemysl Jaroš <xzf (zavináč) seznam.cz>