Table Of Contents

Previous topic

gluPerspective

Next topic

Irrlicht

This Page

glFrustnum

Note

列ベクトル方式で説明

このページでは、 単純な形からglFrustnumの行列を合成していこうと思う。

遠くのものほど小さくなるようにする(透視図法・遠近法, perspective)

ビュー座標(まだ右手系)で、z=dの平面を投影面とすると 投影面の向こう(z<d)にある点p(x, y, z)は、点p’(x’, y’, d)に投影される。 このとき、三角形の相似を利用してx, yの投影平面上の位置を計算できる。

x'=\frac{x \cdot d}{z} \\
y'=\frac{y \cdot d}{z}

これは元の座標をdでスケーリングしてからzでわることで実現できる。 z, wにも同じ計算を適用して

z'=\frac{z \cdot d}{z}=d \\
w'=\frac{w \cdot d}{z}

ここで、w値に除算する値をいれておけばいいじゃないということになり

x'=x \\
y'=y \\
z'=z \\
w'=\left(\frac{d}{z}\right)^{-1}=\frac{z}{d}

これを正規化(w値が1になるように割り算する。遠近除算)すると

x''=\frac{x \cdot d}{z} \\
y''=\frac{y \cdot d}{z} \\
z''=\frac{z \cdot d}{z}=d \\
w''=1

となり具合がいい。

これを行列で得たい。

P \left( \begin{array}{c} x \\
y \\
z \\
1 \end{array} \right) =\left( \begin{array}{c} x \\
y \\
z \\
\frac{z}{d} \end{array}\right)

wにz/dが入るようにするには

P
=\left( \begin{array}{cccc}
1  & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & \frac{1}{d} & 0 \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)

これで座標{x, y, z, 1}に乗算してから正規化すると遠近感(遠くが小さくなる)がつくようになった。

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

今の行列だと正規化するとZが一律-1になり奥行きが失われてしまう。 ここで(x, y, near)を変換するとzが-1になるように(x, y, far)を変換するとzが+1になるようにする。 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}{c} 0 \\
0 \\
near \\
1 \end{array}\right) \\
z'=A \cdot near + B \\
w'=-near

z’を正規化してこの時の値を-1にすると決めたので

z''=\frac{A \cdot near + B}{-near}=-1 \\
A \cdot near + B = near

z=farのとき

\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}{c} 0 \\
0 \\
far\\
1 \end{array}\right) \\
z'=A \cdot far + B \\
w'=-far

z’を正規化してこの時の値を1にすると決めたので

z''=\frac{A \cdot far + B}{-far}=1 \\
A \cdot far + B = -far

AとBを求める

2式を解いて

A \cdot (near-far)=near+far \\
A=\frac{near+far}{near-far}

次にBを求める

z''=A \cdot far + B=-far

Aを代入

z''=\frac{near+far}{near-far}\cdot far + B=-far \\
B=-far-\frac{near+far}{near-far}\cdot far
=\frac{-near \cdot far + far \cdot far - near \cdot far - far \cdot far}{near - far}
=\frac{-2near \cdot far}{near-far}

A、Bをnearとfarの式で置き換えることができた。

\left( \begin{array}{cccc}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & \frac{ far + near }{ near - far } & \frac{ -2near \cdot far}{ near - far} \\
0 & 0 & -1 & 0
\end{array} \right)

ここまでで適用してから正規化すると遠近感がついてZが-1~+1になる行列ができた。 Zの+方向を反転している(遠い方が+)ためこの行列を適用した座標は左手系になるがあまり気にすることもない?

x, yのスケーリング

(移動は省略) x, yともに[-1, +1]におさまるようにスケーリングする。 実際にはz=nearの地点で[-near, +near]になるようにスケーリングする。 要するに、z=nearのときにx=2near(right-left)とy=2near(top-bottom)となるようにスケーリング行列を追加するだけ。

\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)

ここで注意を要するのは、near値と(right-left)値の大きさの関係で左右の視野角、near値と(top-bottom)値の大きさの関係で上下の視野角を指定することになること。これがわかりにくいために[[gluPerspective:http:gluperspective.html]]関数がある。 fovyとnear値によりtop-bottomの大きさを計算し、さらにaspect値からright-leftの大きさを得るのがgluPerspective関数。

最後に

ここで最後の関門。 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)

これでright+left=0かつtop+bottom=0の時限定だけど、glFrustumの式になった。 左右と上下が非対称の場合は、x, yのスケーリング時に移動要素が加わることになる。

参考リンク

http://angra.blog31.fc2.com/blog-entry-114.html

今回のはこちらに書いてあるもの(DirectX仕様の行優先)を ベースにOpenGL風味(列優先)に解読したものであります。

inserted by FC2 system