三次元日誌 RSSフィード

2008-11-11

[]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<d)にある点p(x, y, z)は、点p'(x', y', d)に投影される。

このとき、三角形の相似を利用して

 x’=¥frac{x ¥cdot d}{z} ¥¥ y’=¥frac{y ¥cdot d}{z}

が求められる。

この変換を行列で表現することができて、

¥left( ¥begin{array}{cccc}   1 & 0 & 0 & 0 ¥¥   0 & 1 & 0 & 0 ¥¥ 0 & 0 & 1 & 0   ¥¥ 0 & 0 & 1/d & 0¥end{array} ¥right)¥left(¥begin{array} x ¥¥ y ¥¥ z ¥¥ 1 ¥end{array}¥right) = ¥left(¥begin{array} x ¥¥ y ¥¥ z ¥¥ z/d ¥end{array}¥right) = ¥left(¥begin{array} ¥frac{x ¥cdot d}{z} ¥¥ ¥frac{y ¥cdot d}{z} ¥¥ d ¥¥ 1 ¥end{array}¥right)

となる。

ここで簡単のためdを-1に決め打ちします。

 P = ¥left( ¥begin{array}{cccc} 1 & 0 & 0 & 0 ¥¥ 0 & 1 & 0 & 0 ¥¥ 0 & 0 & 1 & 0 ¥¥ 0 & 0 & -1 & 0 ¥end{array} ¥right)

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

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/ousttrue/20081111/1226368685