[OpenGL] 行列計算
はじめに
ここではOpenGLにおける行列変換に関する説明を行います.行列変換といえば,難しそうですが,OpenGLのAPIにのっとって簡単に説明したいと思います.

まずOpenGL上で簡単にできる行列計算は以下の3つです.
下の図はティーポットが何の行列計算も加えていないものです.
平行移動
glTranslatef(0,0,3); //Z軸方向へ3移動 glutSolidTeapot(1); //ティーポットを描画

回転
glRotatef(45,0,1,0); //Y軸周りに45°回転 glutSolidTeapot(1); //ティーポットを描画

拡大縮小
glScalef(2,1,1); //X軸方向に2倍,Y軸方向に1倍,Z軸方向に2倍 glutSolidTeapot(1); //ティーポットを描画

回転と平行移動の組み合わせ〜平行移動→回転
glRotatef(45,0,1,0); //Y軸周りに45°回転 glTranslatef(0,0,3); //Z軸方向へ3移動 glutSolidTeapot(1); //ティーポットを描画

回転と平行移動の組み合わせ〜回転→平行移動
glTranslatef(0,0,3); //Z軸方向へ3移動 glRotatef(45,0,1,0); //Y軸周りに45°回転 glutSolidTeapot(1); //ティーポットを描画

と,ぜんぜん違う結果になります.詳しくことは,行列計算は右から乗算していくからなのですが,意識せずに,先にしたい行列計算は後に書くという風に考えると簡単です.拡大縮小のglScaleもいっしょです.
つまり先に書いたものは後のオブジェクトすべてに影響するということになります.しかし,これでは非常に不便です.オブジェクトをいくつも描画するときに一つ目はこう,2つ目はこうと,行列計算する方が絶対に楽です.1つ目の行列計算の影響を考えながら,2つ目以降をコーディングするのはすごく手間のかかる作業になります.そこでglPushMatrixとglPopMatrixが活躍します.
glPushMatrixとglPopMatrix
たとえば,黄色のティーポットをY軸まわりに45°回転させ,水色のティーポットをX軸方向に4平行移動したいとします.このとき,こういうコードを書くと間違いです.
//1つ目のティーポット glRotatef(45,0,1,0); //Y軸周りに45° glColor3f(1.0,1.0,0); //色設定 glutSolidTeapot(1); //ティーポット描画 //2つ目のティーポット glTranslatef(4,0,0); //X軸方向へ4移動 glColor3f(0.0,1.0,1.0); //色設定 glutSolidTeapot(1); //ティーポット描画
描画結果はこうなります. [#t7f7897b]

これでは水色のティーポットが平行移動した後,回転してしまっています.これではダメです.なんとかしましょう.これの打開策として,こんな方法があります.
//1つ目のティーポット glRotatef(45,0,1,0); //Y軸周りに45° glColor3f(1.0,1.0,0); //色設定 glutSolidTeapot(1); //ティーポット描画 //2つ目のティーポット glRotatef(-45,0,1,0); //Y軸周りに-45° glTranslatef(4,0,0); //X軸方向へ4移動 glColor3f(0.0,1.0,1.0); //色設定 glutSolidTeapot(1); //ティーポット描画
これを実行すると,ちゃんと希望どおりの位置に水色のティーポットが描画されます.回転するものだから,その手前で逆回転させてやればいいという安直なコードです.しかし,何かしらうっとおしいです.そこでglPushMatrixとglPopMatrixの登場となります.
コーディングはこのようになります.
//1つ目のティーポット glPushMatrix(); glRotatef(45,0,1,0); //Y軸周りに45° glColor3f(1.0,1.0,0); //色設定 glutSolidTeapot(1); //ティーポット描画 glPopMatrix(); //2つ目のティーポット glTranslatef(4,0,0); //X軸方向へ4移動 glColor3f(0.0,1.0,1.0); //色設定 glutSolidTeapot(1); //ティーポット描画
これを実行すると,希望どおりの位置にティーポットたちが描画されます.

こんな風になります.では,glPushMatrixとglPopMatrixの意味を説明しましょう.極論で簡単にいうとglPushMatrixとglPopMatrixで囲んだ間の行列計算はglPopMatrix以下の行列計算に影響しないということになります.実際は行列のメモリのスタックをプッシュ,ポップしているだけなのです.たとえば,以下のコードだとどうなるでしょうか?
glScalef(2,1,2); //1つ目のティーポット glPushMatrix(); glRotatef(45,0,1,0); //Y軸周りに45° glColor3f(1.0,1.0,0); //色設定 glutSolidTeapot(1); //ティーポット描画 glPopMatrix();
glRotatef(90,1,0,0); //2つ目のティーポット glPushMatrix(); glTranslatef(4,0,0); //X軸方向へ4移動 glColor3f(0.0,1.0,1.0); //色設定 glutSolidTeapot(1); //ティーポット描画 glPopMatrix();
まず1つ目のティーポットはY軸周りに45°回転した後,上位のglScalefが計算され,X軸方向へ2倍,Y軸方向へ1倍,Z軸方向へ2倍されます.
2つ目のティーポットはX軸方向へ4移動した後,X軸周りに90°回転し,そのあとX軸方向へ2倍,Y軸方向へ1倍,Z軸方向へ2倍されます.
glPushMatrixとglPopMatrixの紹介でした.実際のメモリ上での挙動やエラーなどについてはリファレンス,赤本などを参考にしてください.