機械学習基礎理論独習

誤りがあればご指摘いただけると幸いです。数式が整うまで少し時間かかります。リンクフリーです。

勉強ログです。リンクフリーです
目次へ戻る

透視投影

透視投影とは

ビュー座標系からクリッピング座標系への変換を投影変換といい、透視投影とはその1種です。
具体的には、空間上に錐台の視体積(視錐台)を定め、それを原点が中心とした辺の長さが2の直方体(標準視体積)へ移す変換のことです。

透視投影変換行列 - 導出その1

ビュー座標系は右手座標系、正規化デバイス座標系は左手座標系とします。
ビュー座標系における視体積を定めるため、各軸の最小値最大値を決めます。
ビュー座標系における視体積の前方面のxy軸の最小値最大値をそれぞれ left,right,bottom,top 、前方面のz値を near、 後方面のz値を far とします。

まずは、zを-1倍することにより、右手座標系から左手座標系へ変換します。
この変換により、near,far-near,-far となります。

ここから透視投影の変換をやっていきます。
P の座標を (x,y,z) とします。
P を前方面に投影した点 P' の座標を (x',y',-near) をとします。
P を正規化デバイス座標系に変換した点 P'' の座標を (x'',y'',z'') とします。


まずは、x'',y'' を求めてます。

前方面に投影されているので x'y' 座標は以下のようになります。

\begin{eqnarray}
&&x'=-\dfrac{near}{z}x\tag{1}\\
&&y'=-\dfrac{near}{z}y\tag{2}
\end{eqnarray}
x',y' は前方面上の点であるので、x'\in[left, right],y' \in[bottom,top] です。
これを [-1,1] に移せばよいので、x'',y'' 座標は以下のようになります。
\begin{eqnarray}
&&x''=\frac{2(x'-left)}{right-left}-1\tag{3}\\
&&y''=\frac{2(y'-bottom)}{top-bottom}-1\tag{4}
\end{eqnarray}
(1)(3) に代入し、(2)(4) に代入して整理します。
\begin{eqnarray}
&&x''=\frac{-2near}{right-left}\cdot\frac{x}{z}+\frac{right+left}{right-left}\tag{5}\\
&&y''=\frac{-2near}{top-bottom}\cdot\frac{y}{z}+\frac{top+bottom}{top-bottom}\tag{6}\\
\end{eqnarray}
次に z'' を求めます。
z''1/z の関数になるように作る必要があるので、以下のようになります。
\begin{eqnarray}
z''=\frac{A}{z}+B\tag{7}
\end{eqnarray}
z\in[-near, -far] であり、これを [-1,1] に移せばよいので、以下の式が成り立ちます。
\begin{eqnarray}
\left\{
\begin{array}{l}
 -1=\dfrac{A}{-near}+B\\
1=\dfrac{A}{-far}+B
\end{array}
\right.\tag{8}
\end{eqnarray}

(8)A,B について解きます。

\begin{eqnarray}
\left\{
\begin{array}{l}
A=\dfrac{2far\cdot near}{far-near}\\
B=\dfrac{far+near}{far-near}
\end{array}
\right.\tag{9}
\end{eqnarray}

(9)(7) に代入します。

\begin{eqnarray}
z''=-\dfrac{2near\cdot far}{far-near}\left(-\dfrac{1}{z}\right)+\frac{far+near}{far-near}\tag{10}
\end{eqnarray}

x'',y'',z'' が求まったので、これを w=-z として、同次座標にします。

\begin{eqnarray}
&&-zx''=\dfrac{2near}{right-left}x+\dfrac{right+left}{right-left}z\\
&&-zy''=\dfrac{2near}{top-bottom}y+\dfrac{top+bottom}{top-bottom}z\\
&&-zz''=-\dfrac{far+near}{far-near}z-\frac{2far\cdot near}{far-near}\\
\end{eqnarray}

以上より、透視投影変換行列 {\bf M}_{projection} は以下のようになります。

\begin{eqnarray}
{\bf M}_{projection}=\begin{pmatrix}
\dfrac{2near}{right-left} & 0 & \dfrac{right+left}{right-left} & 0\\
0 & \dfrac{2near}{top-bottom} & \dfrac{top+bottom}{top-bottom} & 0\\
0 & 0 & -\dfrac{far+near}{far-near} & -\dfrac{2far\cdot near}{far-near}\\
0 & 0 & -1 & 0
\end{pmatrix}\tag{11}
\end{eqnarray}

透視投影変換行列 - 導出その2

左手座標系に変換するところまでは、導出その1と同じです。
まず、前方面上の矩形の表示領域の中心をz軸を通るようにします。
これは、xy軸方向において「横に押し出すような変形」なので、せん断変換を行います。
この変換行列 {\bf M}_{share} は以下のようになります。

\begin{eqnarray}
{\bf M}_{share}=
\begin{pmatrix}
1 & 0 & \dfrac{right+left}{2near} & 0\\
0 & 1 & \dfrac{top + bottom}{2near} & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & 1\\
\end{pmatrix}\tag{12}
\end{eqnarray}

次に、投影します。
P の座標を (x,y,z) とします。
P を前方面に投影した点 P' の座標を (x',y',z') をとします。

まずxy座標から考えましょう。
前方面に投影されているので x'y' 座標は以下のようになります。

\begin{eqnarray}
&&x'=-\dfrac{near}{z}x\tag{13}\\
&&y'=-\dfrac{near}{z}y\tag{14}
\end{eqnarray}
(13),(14) の変換で後方面のxy座標が前方面のxyと一致します。

次にzですが、zは、zに反比例するので、-\dfrac{far\cdot near}{z} と変換します。
その後、zの中心が原点に平行移動すると、-\dfrac{far\cdot near}{z}-\dfrac{far+near}{2} となります。
この変換により、-near\rightarrow \dfrac{far-near}{2},-far\rightarrow \dfrac{near-far}{2} となっています。
この変換行列 {\bf M}_{perspective} は以下のようになります。

\begin{eqnarray}
{\bf M}_{perspective}=
\begin{pmatrix}
near & 0 & 0 & 0\\
0 & near & 0 & 0\\
0 & 0 & \dfrac{far+near}{2} & far\cdot near\\
0 & 0 & -1 & 0\\
\end{pmatrix}\tag{15}
\end{eqnarray}
ここまでの変換行列 {\bf M}_{perspective}{\bf M}_{share} を掛けた視体積は、以下の図のようになります。

最後に、視体積の辺の長さを2にします。
このときの変換行列 {\bf M}_{scaling} は以下のようになります。

\begin{eqnarray}
{\bf M}_{scaling}=
\begin{pmatrix}
\dfrac{2}{right-left} & 0 & 0 & 0\\
0 & \dfrac{2}{top-bottom} & 0 & 0\\
0 & 0 & -\dfrac{2}{far-near} & 0\\
0 & 0 & 0 & 1
\end{pmatrix}\tag{16}
\end{eqnarray}

透視投影変換行列 {\bf M}_{projection}{\bf M}_{share},{\bf M}_{perspective},{\bf M}_{scaling} を掛け合わせたものとなります。

\begin{eqnarray}
{\bf M}_{projection}&=&{\bf M}_{scaling}{\bf M}_{perspective}{\bf M}_{share}\\
&=&
\begin{pmatrix}
\dfrac{2}{right-left} & 0 & 0 & 0\\
0 & \dfrac{2}{top-bottom} & 0 & 0\\
0 & 0 & -\dfrac{2}{far-near} & 0\\
0 & 0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
near & 0 & 0 & 0\\
0 & near & 0 & 0\\
0 & 0 & \dfrac{far+near}{2} & far\cdot near\\
0 & 0 & -1 & 0\\
\end{pmatrix}
\begin{pmatrix}
1 & 0 & \dfrac{right+left}{2near} & 0\\
0 & 1 & \dfrac{top + bottom}{2near} & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & 1\\
\end{pmatrix}\\
&=&\begin{pmatrix}
\dfrac{2near}{right-left} & 0 & \dfrac{right+left}{right-left} & 0\\
0 & \dfrac{2near}{top-bottom} & \dfrac{top+bottom}{top-bottom} & 0\\
0 & 0 & -\dfrac{far+near}{far-near} & -\dfrac{2far\cdot near}{far-near}\\
0 & 0 & -1 & 0
\end{pmatrix}\tag{17}
\end{eqnarray}

最後に

near,farの値については、平行投影の記事を参照してください。
せん断の図を添えたい。
透視深度についてもう少しくどく解説してもよいかもしれない。
fovyの透視投影は別記事にしようかな?(未定)

参考文献

コンピュータグラフィックスの基礎 p55-p60
ゲームプログラミングのための3Dグラフィックス数学 p89-p100
Real-Time Rendering, Third Edition p92-p96

目次へ戻る