(2) gluLookAt はどんな matrix を生成するのか? | Chandler@Berlin

Chandler@Berlin

ベルリン在住

gluLookAt matrix
Gimbal lock はなぜ(いつ)起こるのか?


カメラの方向は回転の matrix,移動は translation matrix によって表現される.OpenGL ではカメラの位置と方向の default というものが既定されていて,それを動かして使うからである.カメラは最初原点にあって,Z の負の方向を見ており,Y の正方向を上にしている.回転と移動の matrix はぞれ次のような形をしている.
Chandler@Berlin-TR matrix
ここで,

- x: カメラの x軸,(前回のblogの)図中では right, プログラム中では side
- y: カメラの y軸,(前回のblogの)図中では up, プログラム中では up
- z: カメラの z軸,(前回のblogの)図中では Z, プログラム中では -forward
- e: カメラの位置,(前回のblogの)図中では eye, プログラム中では eyex, eyey, eyez

プログラムでは,eye, lookat,upが与えられるので,

z = normalize(eye - lookat)
x = normalize(cross(up,z))
y = normalize(cross(z, x))

として計算されている.cross は外積であり,normalize はベクトルの正規化の関数を示す.計算が z, x, yの順番になっていることに注意すること.

ところで,この matrix では z が -view の方向であることに気がつかれただろうか.これは OpenGL の depth で言えば depth の負の方向である.つまりカメラの基底座標はカメラのレンズの方向ではなく,後ろを向いているのである.しかもカメラから遠いものほど depth は大きくなるようになっているが,この depth が正のものは,world 座標では負になる.座標系は単なる表現であるので間違いではない.たとえば viewport の座標は Y は正が上であるが,スクリーンでは Y は下向きが正である.この world 座標系とカメラの座標系の違いは私はよく混乱する.


では,R がなぜ回転と呼ばれているのかを説明しよう. x, y,z はそれぞれ直交するように外積で作られている.また,これらは全て正規化されていて長さが 1 のベクトルになっている.これが回転と呼ばれているのは,長さを変えずに,方向だけ変化させているからである.私としては回転として考えることよりも,座標軸の変換として考えた方が直感的な気がする.それは回転の場合には回転軸とか動いている様子とかを私は考えてしまい,それがこの基底ベクトル中には見えにくいからである.R が座標軸を変換するという意味は,standard な基底をこの R に掛けると,以下のようになるからである.
Chandler@Berlin-whyRisRotating

つまり,[1 0 0 0]^T は x に,[0 1 0 0]^T はyに,[0 0 1 0]^T は z に変換されている.良くみると, [2 0 0 0]^T は 2x にと,各軸の長さも保存される.これが座標変換という意味である.そしてこういう変換は長さが変換せずに方向だけ変化するので,実際には剛体を回転することと同じである.

あとはカメラの位置の分だけ移動すれば良い.TR の式に内積が出てきているのは,実際に計算するとわかるだろうが,ある位置を新しい座標で示すというのは,図 2に示すように,各座標軸への投影である.したがってcos の計算がでてくる.つまり内積の登場となる.

$Chandler@Berlin-basisprojection
Figure 2: Basis transformation and dot product

OpenGL の gluLookAt の作る matrix がどういうものか,私なりにソースコードから解釈してみた.このカメラの基底の行列を上手く使うと,カメラをトラックボール上に配置して眺めるような navigation ができる.3D の物体を観測するようなプログラムを書く場合,最初に gimbal lock (ギンバルロック,ジンバルロック)を避けるようなプログラムを書くことになるだろう.gimbal lock は,視線方向が up vectorに近くなることで発生する.これを避けるには,単純にカメラの基底自体を回転し,全ての基底が常に直交するように気を配れば良い.図 3の (b) のように,視線方向のみ変化させるような実装は簡単だが,ある場所でレンズが折れてしまう.これを避けるには,単純に(c)のようにカメラを回転させることである.


$Chandler@Berlin
Figure 3: Gimbal lock camerta. When you want to see a bit upper direction, (b) bending the lens implementation, this causes the gimbal lock, (c) rotate the camera, no gimbal lock

次回はおまけとして python での gluLookAt の実装を紹介する.これは最初に述べたように,OpenGL と自分の OpenGL に依存しない renderer を混合させたい時などに使う.