glFrustumの解読

追記: 2010/3/24
書いたときの理解が足りないため意味不明なところがある。
少し書き直した。
http://gunload.web.fc2.com/opengl/tutorial/glfrustum.html


glFrustumが何をしているかというと
View座標(視点を原点として視線方向がz軸の負の方向を向いている右手系の座標系)に
おいて指定された視錘台(frustum)を正規Device座標に変換している。

(left, bottom, near)->(-1, -1, -1)
(right, top, far)->(1, 1, 1)

ここで正規Device座標ではZ軸が反転されて左手系になっていることに注意が必要。
(near -> far がZ軸の負の方向だったのが、正の方向になっている)
単純な形からglFrustnumの行列を合成していこうと思う。
あとright+left=0かつtop+bottom=0の場合限定。

同次座標を使った投影行列

ビュー座標(まだ右手系)で、z=d(d<0)の平面を投影面とすると
投影面の向こう(z

Z軸を[-1, +1]に収まるようにする

つまり(x, y, near)を変換するとzが+1になるように
(x, y, far)を変換するとzが-1になるようにする。
(まだZを反転しておらず右手系)
Z軸のみの問題なので関係部分A(スケール), B(移動)とおいておく。
ここでz=nearのとき
\left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & A & B \\ 0 & 0 & -1 & 0 \end{array} \right)\left(\begin{array} 0 \\ 0 \\ near \\ 1 \end{array}\right) \\ B = - near \left( A + 1 \right)
BをAとnの式に置き換えることができる。
z=farのとき
\left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & A & - near \left( A + 1 \right) \\ 0 & 0 & -1 & 0 \end{array} \right)\left(\begin{array} 0 \\ 0 \\ far\\ 1 \end{array}\right) \\ A = \frac{ far + near }{ far - near } \\ B = \frac{ -2far \cdot near}{ far - near}
A、Bをnearとfarの式で置き換えることができた。
\left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{ far + near }{ far - near } & \frac{ -2far \cdot near}{ far - near} \\ 0 & 0 & -1 & 0 \end{array} \right)

Z軸の反転

\left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & -1 & 0 \\ 0 & 0 & 0 & 1 \end{array} \right) \left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{ far + near }{ far - near } & \frac{ -2far \cdot near}{ far - near} \\ 0 & 0 & -1 & 0 \end{array} \right) = \left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & - \frac{ far + near }{ far - near } & \frac{ 2far \cdot near}{ far - near} \\ 0 & 0 & -1 & 0 \end{array} \right)
以降左手系となる。

x, yのスケーリング

x, yともに[-1, +1]におさまるようにスケーリングする。
実際にはz=nearの地点で[-near, +near]になるようにスケーリングする。
\left( \begin{array}{cccc} \frac{2near}{right - left} & 0 & 0 & 0 \\ 0 & \frac{2near}{top-bottom} & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{array} \right)\left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & - \frac{ far + near }{ far - near } & \frac{ 2far \cdot near}{ far - near} \\ 0 & 0 & -1 & 0 \end{array} \right) = \left( \begin{array}{cccc} \frac{2near}{right - left} & 0 & 0 & 0 \\ 0 & \frac{2near}{top-bottom} & 0 & 0 \\ 0 & 0 & - \frac{ far + near }{ far - near } & \frac{ 2far \cdot near}{ far - near} \\ 0 & 0 & -1 & 0 \end{array} \right)

最後に

ここで最後の関門。
glFrustumの定義と比べると第3行第4列の符号が合いません。
何故かというと引数のnear, farは視点からの距離をあらわしていて
視点座標では負の値になるところを正の値をとっているからです。
そこでnear=-near'、far=-far'と置き換えます。
\left( \begin{array}{cccc} \frac{2near}{right - left} & 0 & 0 & 0 \\ 0 & \frac{2near}{top-bottom} & 0 & 0 \\ 0 & 0 & - \frac{ -far' -near' }{ -far' + near' } & \frac{ 2 \left( - far' \right) \left( - near' \right)}{ - far' + near'} \\ 0 & 0 & -1 & 0 \end{array} \right) \\ = \left( \begin{array}{cccc} \frac{2near}{right - left} & 0 & 0 & 0 \\ 0 & \frac{2near}{top-bottom} & 0 & 0 \\ 0 & 0 & - \frac{ far' + near' }{ far' - near' } & \frac{ -2 far' \cdot near' }{ far' - near'} \\ 0 & 0 & -1 & 0 \end{array} \right)
これでglFrustumの式になりました。
(right+left=0かつtop+bottom=0の時限定)


以上でglFrustumの解読完了。
right+left!=0やtop+bottom!=0の歪んだ投影は必要になったときにまた考えるとしよう。

参考リンク

http://angra.blog31.fc2.com/blog-entry-114.html
今回のはこちらに書いてあるもの(DirectX仕様の行優先)を
ベースにOpenGL風味(列優先)に解読したものであります。

参考文献

リアルタイム レンダリング 第2版


texを埋めてアップしておきました。
http://gunload.web.fc2.com/programming/3D/opengl/glFrustum.html