あらためて行列の動作を確認

たまに使うと細かいことをいろいろ忘れていて難渋するので確認しておくことにした。

まず、前提として座標系は右手系。
(DirectXは逆だそうな)

行列は列優先(row-major)で左から行列を、右から列ベクトルをかけると考える。
(DirectXは逆だそうな)

さらに行列がこうあったとすると
\left( \begin{array}{cccc} m11 & m12 & m13 & m14 \\ m21 & m22 & m23 & m24 \\ m31 & m32 & m33 & m34 \\ m41 & m42 & m43 & m44 \end{array} \right)
配列としての格納方法は、

double matrix[16]={ m11, m12, m13...}

じゃなくて

double matrix[16]={ m11, m21, m31...}

ここに書いてある。
http://www.opengl.org/sdk/docs/man/xhtml/glLoadMatrix.xml


次のようなコードで動作確認をしてみた。
glTranslatedなどを実行してOpenGLから行列を抜き出して表示するだけであります。

#include <iostream>
#include <GL/glut.h>


void printMatrix(const float *m)
{
  std::cout.setf(std::ios::fixed);
  std::cout 
    << '[' <<  m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << ']' << std::endl
    << '[' <<  m[4] << ',' << m[5] << ',' << m[6] << ',' << m[7] << ']' << std::endl
    << '[' <<  m[8] << ',' << m[9] << ',' << m[10] << ',' << m[11] << ']' << std::endl
    << '[' <<  m[12] << ',' << m[13] << ',' << m[14] << ',' << m[15] << ']' << std::endl
    << std::endl;
};

void printOpenGLMatrix(const int mode)
{
  float m[16]; 
  glGetFloatv(mode, m);
  printMatrix(m);
}

void display(void)
{
  glClear(GL_COLOR_BUFFER_BIT);
  glFlush();

  // MODEL VIEW
  //------------------------------------------------------------//
  std::cout << "MODELVIEW_MATRIX: " << std::endl;
  printOpenGLMatrix(GL_MODELVIEW_MATRIX);

  // translate
  std::cout << "translate..." << std::endl;
  glMatrixMode(GL_MODELVIEW);
  glTranslated(1.0, 2.0, 3.0);

  std::cout << "MODELVIEW_MATRIX: " << std::endl;
  printOpenGLMatrix(GL_MODELVIEW_MATRIX);

  // translate + scale
  std::cout << "translate + scale ..." << std::endl;
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glTranslated(1.0, 2.0, 3.0);
  glScaled(2.0, 2.0, 2.0);
  std::cout << "MODELVIEW_MATRIX: " << std::endl;
  printOpenGLMatrix(GL_MODELVIEW_MATRIX);

  // scale + translate
  std::cout << "scale + translate ..." << std::endl;
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glScaled(2.0, 2.0, 2.0);
  glTranslated(1.0, 2.0, 3.0);
  std::cout << "MODELVIEW_MATRIX: " << std::endl;
  printOpenGLMatrix(GL_MODELVIEW_MATRIX);

  // PROJECTION
  //------------------------------------------------------------//
  // perse pective
  std::cout << "persepective..." << std::endl;
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  //gluPerspective(30.0, 1.5, 1.0, 100.0);
  glFrustum(-1.0, 2.0, -1.0, 4.0, 1.0, 100.0);

  std::cout << "PROJECTION_MATRIX: " << std::endl;
  printOpenGLMatrix(GL_PROJECTION_MATRIX);
}

void init(void)
{
  glClearColor(0.0, 0.0, 1.0, 1.0);
}

int main(int argc, char *argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA);
  glutCreateWindow(argv[0]);
  glutDisplayFunc(display);
  init();
  glutMainLoop();
  return 0;
}

出力はこんな感じ

MODELVIEW_MATRIX:
[1.000000,0.000000,0.000000,0.000000]
[0.000000,1.000000,0.000000,0.000000]
[0.000000,0.000000,1.000000,0.000000]
[0.000000,0.000000,0.000000,1.000000]

translate...
MODELVIEW_MATRIX:
[1.000000,0.000000,0.000000,0.000000]
[0.000000,1.000000,0.000000,0.000000]
[0.000000,0.000000,1.000000,0.000000]
[1.000000,2.000000,3.000000,1.000000]

translate + scale ...
MODELVIEW_MATRIX:
[2.000000,0.000000,0.000000,0.000000]
[0.000000,2.000000,0.000000,0.000000]
[0.000000,0.000000,2.000000,0.000000]
[1.000000,2.000000,3.000000,1.000000]

scale + translate ...
MODELVIEW_MATRIX:
[2.000000,0.000000,0.000000,0.000000]
[0.000000,2.000000,0.000000,0.000000]
[0.000000,0.000000,2.000000,0.000000]
[2.000000,4.000000,6.000000,1.000000]

translateの後にscaleを適用するのが
T・S
で、
scaleしてからtranslateが
S・T
となった。

これはまさに左から行列を、右から列ベクトルをかけて作用させる数学の本などに載っている方式。
後から適用したOpenGLの操作を行列として右からかけていくと理解すればよいのか。
書いてみたら腑に落ちた。なるほど。
注意するのは行列を配列に格納するときの方向で、
ここだけ普通のCのやり方ではなく、いわゆるFortran方式になっていることだ。
こっちの方が計算効率良かったりするのかな。


この辺は直接的に行列の操作をしなければあまり気にしなくてもよいのだけど、
曖昧な理解のままglLoadMatrixd等を使うとはまります。
ちなみにわたしはglLoadTransposeMatrixdとか使います。
そのためだけにglext.hを使っているのもどうかと思うが。


ついでなので次は
gluPerspectiveとglFrustumの動作について確認しておきたい。