HTML

Az élet kódjai

Csináld maga. Senki nem csinálja meg helyetted.

Friss topikok

OpenGL 4.1 mátrixai

2011.07.31. 22:22 Travis.CG

A 3.1-es verzió óta az OpenGL nem tartalmazza a forgatással, eltolással kapcsolatos rutinokat. A profik azt írják, hogy ez egy jó ötlet volt, mert nem zárja korlátok közé a programozót. Egy API-nak viszont az a feladata, hogy megkönnyítse egy programozó életét. Ha egy alapvető funkcionalitás hiányzik az API-ból, akkor programozók tömegét kényszerítjük arra, hogy ugyan azt a feladatot elkészítse.

Aki nem akar saját maga bajlódni a mátrixok számolásával, az használhatja a glm-et, vagy egy korábbi posztomban említett Nopper úr GLUS könyvtárát. Én viszont egy merészet gondoltam, és úgy döntöttem, megírom saját rutinjaimat. Azért nevezem merésznek az ötletet, mert nem sokat konyítok a matematikához. Igyekeztem faék egyszerűségű kódot írni, amitől lehet, hogy a kód használhatósága nehézkes lesz. A megértést viszont biztosan nem fogja gátolni.

Az első és legfontosabb megemlíteni, hogy az OpenGL oszlopfolytonosan várja a mátrixokat, ami a Wikipédiában fellelhető mátrixok transzponáltja. Érthetően fogalmazva:

0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15

 Legyenen az indexek. Az általam használt kódban minden mátrix egy 16 elemű tömb lesz.

Egységmátrix

A legalapvetőbb mátrix a 3D grafikában. Az átló mentén 1-t tartalmaz. Erre azért van szükség, mert a mátrixok szorzása alkalmával nem fogja egyik mátrix a másikat kinullázni. Minden további mátrix ebből indul ki, külön nem fogom leírni.

Perspektíva

A perspektíva viszonylag egyszerű. A képernyő oldalaránya és a látószög beállítása mellett kiszámítja a mátrix értékeit. Két további paramétere a közeli és távoli vágási távolság.

void perspectiveMatrix(float *m, float fov, float aspect, float near, float far){
  float range;

  range = tan(fov * M_PI/360.0) * near;
  memset(m, 0, sizeof(float) * 16);
  m[0] = (2.0 * near) / ((range * aspect) - (-range * aspect));
  m[5] = (2.0 * near) / (2.0 * range);
  m[10] = -(far + near) / (far - near);
  m[11] = -1.0;
  m[14] = -(2.0 * far * near) / (far - near);
}

Kamera pozíció

Egy demóban, megkockáztatom ez az egyik legfontosabb mátrix. Segítségével megadhatjuk, hogy honnan nézzük a jelenetet. Ennek megfelelően bonyolult, mert több vektorműveletet kell végezni.

void lookAt(float *m, float *eye, float *target, float *up){
   int i;
   float n[3];
   float u[3];
   float v[3];
   float d[3];

   for(i = 0; i < 3; i++) n[i] = eye[i] - target[i];
   crossproduct3v(up, n, u);
   crossproduct3v(n, u, v);
   normalize(u, 3);
   d[0] = dotproduct(eye, u, 3) * -1.0;
   normalize(v, 3);
   d[1] = dotproduct(eye, v, 3) * -1.0;
   normalize(n, 3);
   d[2] = dotproduct(eye, n, 3) * -1.0;

   m[0]  = u[0];
   m[4]  = u[1];
   m[8]  = u[2];
   m[12] = d[0];
   m[1]  = v[0];
   m[5]  = v[1];
   m[9]  = v[2];
   m[13] = d[1];
   m[2]  = n[0];
   m[6]  = n[1];
   m[10] = n[2];
   m[14] = d[2];
   m[3]  = 0.0;
   m[7]  = 0.0;
   m[11] = 0.0;
   m[15] = 1.0;
}

Pozíció

A pozíció mátrix a legegyszerűbb. Gyakorlatilag a mátrix három elemét helyettesítjük a koordinátákkal.

void translate(float *m, float x, float y, float z){
   int i;

   /* Identity matrix */
   for(i = 0; i < 16; i++){
      if( (i % 5) == 0){
         m[i] = 1.0;
      }
      else m[i] = 0.0;
   }

   m[12] = x;
   m[13] = y;
   m[14] = z;
}

Forgatás

A forgatás néz ki a legijesztőbben. A legtöbb példaprogram külön veszi a tengelyek mentén történő forgatásokat, majd mátrix szorzásokkal egy forgatási mátrixot képez. Szerencsére lehet találni olyan leírást, ami leírja, hogyan néz ki az a mátrix, amiben elvégezték a mátrix szorzást. A kód ilyesztő, de csak egyszer kell megírni.

 void rotate(float *m, float rx, float ry, float rz){
   int i;
   /* Identity matrix */
   for(i = 0; i < 16; i++){
      if( (i % 5) == 0){
         m[i] = 1.0;
      }
      else m[i] = 0.0;
   }

   /* General rotation */
   m[0]  =  cosf(ry) * cosf(rz);
   m[4]  = -cosf(rx) * sinf(rz) + sinf(rx) * sinf(ry) * cosf(rz);
   m[8]  =  sinf(rx) * sinf(rz) + cosf(rx) * sinf(ry) * cosf(rz);
   m[12] =  0.0;
   m[1]  =  cosf(ry) * sinf(rz);
   m[5]  =  cosf(rx) * cosf(rz) + sinf(rx) * sinf(ry) * sinf(rz);
   m[9]  = -sinf(rx) * cosf(rz) + cosf(rx) * sinf(ry) * sinf(rz);
   m[13] =  0.0;
   m[2]  = -sinf(ry);
   m[6]  =  sinf(rx) * cosf(ry);
   m[10] =  cosf(rx) * cosf(ry);
   m[14] =  0.0;
   m[3]  =  0.0;
   m[7]  =  0.0;
   m[11] =  0.0;
   m[15] =  1.0;

}

Egyéb mátrix műveletek

Most pedig jöjjenek az egyéb nyalánkságok. A kamera pozíciójánál a kódban ilyen függvények láthatóak, mint crossproduct meg dotproduct. Mik ezek? Az első a vektoriális szorzat, aminek érdekessége, hogy csak 3 dimenzióban értelmezik.

void crossproduct3v(float *a, float *b, float *result){
   result[0] = a[1] * b[2] - a[2] * b[1];
   result[1] = a[2] * b[0] - a[0] * b[2];
   result[2] = a[0] * b[1] - a[1] * b[0];
}

A másik a skaláris szorzat, ami meglepő módon egy számot ad vissza. A hossz arányos a két vektor által bezárt szöggel, ezért a fények programozásánál is sokszor előkerül.
float dotproduct(float *a, float *b, int size){
   int i;
   float result = 0.0;

   for(i = 0; i < size; i++) result += a[i] * b[i];
   return(result);
}
Ha programunkban egy térhálós modellt elhelyezünk, forgatunk, és még a kamerát is mozgatjuk, több mátrixot kapunk, de hogyan készítsünk egy mátrixot? A válasz a mátrix szorzás. A mátrix szorzásnál a tagok sorrendje nem felcserélhető. Ezt mindenki ki is próbálhatja, hogy mi történik, ha a modellnél az eltolási mátrixot szorozza a forgatásival, vagy fordítva.

A szorzás sorrendje a következő: perspektíva * forgatás * eltolás * kamera. A szorzást a következő kód valósítja meg:

void matrixMultiply4x4(float *a, float *b, float *c){
   int i,x,y;

   for(i = 0; i < 16; i++){
      x = i & 12;
      y = i % 4;
      c[i] = a[x + 0] * b[y + 0] +
             a[x + 1] * b[y + 4] +
             a[x + 2] * b[y + 8] +
             a[x + 3] * b[y + 12];
   }
}

Mondtam, hogy egyszerű lesz, mint a faék. Hozzáértők a kommentekben mindenféle optimalizációt küldhetnek.

Mátrix betöltése shaderbe

A shaderben a mátrix uniform mat4 deklarációval szerepel. Kódunkban a glGetUniformLocation(shader, változónév) segítségével megkapjuk az erőforrás leíróját (egy egész szám), majd ezt felhasználva a glUniformMatrix4fv(leíró, 1, GL_FALSE, matrixpointer) segítségével beállítjuk azt.

Források

http://en.wikipedia.org/wiki/Transformation_matrix

http://en.wikipedia.org/wiki/Rotation_matrix#General_rotations

http://www.songho.ca/opengl/gl_transform.html

 

2 komment

Címkék: programozás opengl

A bejegyzés trackback címe:

https://cybernetic.blog.hu/api/trackback/id/tr443115369

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

tormanator · http://sugarkovetes.blog.hu/ 2011.09.07. 18:01:45

Ha érdelek még néhány info a dot()-ról, itt megtalálod.

sugarkovetes.blog.hu/

Travis.CG 2011.09.14. 15:21:47

Köszönöm, feltétlenül el fogom olvasni!
süti beállítások módosítása