読者です 読者をやめる 読者になる 読者になる

しゅみぷろ

プログラミングとか

コンピューターグラフィックス基礎のメモ

Rendering Pipeline Graphics Linear Algebra

はじめに

基礎から勉強し直しています(というより基礎すらままならない状態なので...)。 ということで今回は座標系、変換行列、ビューイングパイプラインについて簡単にですがメモしておこうと思います。

変換行列や各座標系に関してはDirectXOpenGLで多少違いがあるので注意が必要です。 たとえば、ここで紹介する変換行列は列優先(OpenGL系)ですが、行優先(DirectX)の場合は行列とベクトルの順序が変わることになり、それに伴って変換行列を転置させる必要があります。変換行列の合成についても、掛け合わせる順序が変わってきます。 UV座標もOpenGLDirectXで異なり、OpenGLの場合原点は左下、DirectXの場合は左上になります。標準視体積についても多少違いがあり、本記事ではOpenGLでの標準視体積を紹介しています。 この他にも様々な違いがあります。

右手系と左手系

f:id:es_program:20160426011026p:plain

デカルト座標で、上図のように右手系、左手系を区別します。 両者はZ軸を基準に鏡に映すようにして変換することで要素を読み替えることができます(chirality)。

座標系

  • WorldSpace
    • Scene内の全てのオブジェクトを含む空間。空間内でのオブジェクトの絶対的な位置を示す座標系。この空間内のオブジェクトをViewSpaceに変換することをView変換といいます。
  • ModelSpace
    • 特定のオブジェクトが持つ固有の座標系。この空間内のオブジェクトをWorldSpaceに変換することをModel変換といいます。
  • ViewSpace
    • カメラを基準とした座標系。この空間内のオブジェクトをProjectionSpaceに変換することをProjection変換といいます。Projection変換は

      • 正規化ビューボリュームを計算(ビューボリュームのScale変換)
      • 正規化ビューボリュームを投影(正規化デバイス座標の計算)

      といったことを行っています。Projection変換したジオメトリは同次座標であるため通常座標を求めるにはジオメトリの第四要素の$w$値で割る必要があります($w$で割った後の$(−1≤x≤1,−1≤y≤1,−1≤z≤1)$の空間を正規化デバイス座標と言います。)

  • ProjectionSpace
    • ビューボリュームをProjection変換し、正規化デバイス座標に直した空間です。この空間内のジオメトリをScreenSpaceに変換することをScreen変換といいます。
  • ScreenSpace
    • 画面左上(OpenGL系では画面左下)を原点として、画面の解像度に対応したピクセル位置を単位とする2Dデカルト座標系です。
  • ViewportSpace
    • ScreenSpaceを正規化した座標です。

2次元座標変換

平行移動

\[x' = x + t_x\]

\[y' = y + t_y\]

ここで、\(t_x,t_y\)は\(x,y\)方向の移動量です。

拡大・縮小

\[x'=s_xx\]

\[y'=s_yy\]

ここで、\(s_x\),\(s_y\)は$x,y$方向の拡大・縮小率。座標系の原点を中心とした変換が行われます

回転

\[x'=xcos\theta-ysin\theta\]

\[y'=xsin\theta+ycos\theta\]

回転も座標系の原点を中心とした変換が行われます

行列表現

平行移動

\[\begin{bmatrix}x'\\y'\end{bmatrix}=\begin{bmatrix}t_x\\t_y\end{bmatrix}+\begin{bmatrix}x\\y\end{bmatrix}\]

回転

\[\begin{bmatrix}x'\\y'\end{bmatrix}=\begin{bmatrix}cos\theta&-sin\theta\\sin\theta & cos\theta\end{bmatrix}\begin{bmatrix}x\\y\end{bmatrix}\]

拡大・縮小

\[\begin{bmatrix}x'\\y'\end{bmatrix}=\begin{bmatrix}s_x&0\\0&s_y\end{bmatrix}\begin{bmatrix}x\\y\end{bmatrix}\]

平行移動のみ変換を行列の積で表現できないため不便です。これを解決するには同次座標系を用います。

同次座標系

幾何学的変換を全て(平行移動を含め)行列の積で表すことができるようになります。同次座標系では、通常座標において\((x,y)\)と表す位置を実数\(w(w\not=0)\)を用いて\((wx,wy,w)\)と表します。例えば通常座標の\((5,7)\)は同次座標では\((5,7,1),(10,15,2),...\)と複数の表し方があります。簡単のため、普通は\(w=1\)とし、2次元の場合\((x,y,1)\)と書きます。3次元の場合は\((x,y,z,1)\)と書きます。

f:id:es_program:20160426011049p:plain

2次元同次座標変換

平行移動

\[\begin{bmatrix}x'\\y'\\1\end{bmatrix}=\begin{bmatrix}1&0&t_x\\0&1&t_y\\0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\1\end{bmatrix}=T(t_x,t_y)\begin{bmatrix}x\\y\\1\end{bmatrix}\]

回転

\[\begin{bmatrix}x'\\y'\\1\end{bmatrix}=\begin{bmatrix}cos\theta&-sin\theta&0\\sin\theta&cos\theta&0\\0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\1\end{bmatrix}=R(\theta)\begin{bmatrix}x\\y\\1\end{bmatrix}\]

拡大・縮小

\[\begin{bmatrix}x'\\y'\\1\end{bmatrix}=\begin{bmatrix}s_x&0&0\\0&s_y&0\\0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\1\end{bmatrix}=S(s_x,s_y)\begin{bmatrix}x\\y\\1\end{bmatrix}\]

\(平行移動変換行列をT(t_x,t_y)、回転行列をR(\theta)、拡大・縮小行列をS(s_x,s_y)としています。\)

アフィン変換

2次元の幾何学的変換の一般的な行列表現は

\[\begin{bmatrix}x'\\y'\\1\end{bmatrix}=\begin{bmatrix}a&b&c\\d&e&f\\0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\1\end{bmatrix}\]

で、このような座標変換をアフィン変換と呼びます(最下段は一番右のみ1で他は0。これにより幾何学的性質が保たれる)。

3次元の場合は

\[\begin{bmatrix}x'\\y'\\z'\\1\end{bmatrix}=\begin{bmatrix}a&b&c&d\\e&f&g&h\\i&j&k&l\\0&0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\z\\1\end{bmatrix}\]

のようになります。

合成変換

点Pに、最初に\(A_1\)、次に\(A_2\)、最後に\(A_3\)の変換を行う場合

\[P'=A_3A_2A_1P\]

となります。

3次元同次座標変換

平行移動

\[\begin{bmatrix}x'\\y'\\z'\\1\end{bmatrix}=\begin{bmatrix}1&0&0&t_x\\0&1&0&t_y\\0&0&1&t_z\\0&0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\z\\1\end{bmatrix}=T(t_x,t_y,t_z)\begin{bmatrix}x\\y\\z\\1\end{bmatrix}\]

回転

  • x軸まわりの回転

\[\begin{bmatrix}x'\\y'\\z'\\1\end{bmatrix}=\begin{bmatrix}1&0&0&0\\0&cos\theta&-sin\theta&0\\0&sin\theta&cos\theta&0\\0&0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\z\\1\end{bmatrix}=R_x(\theta)\begin{bmatrix}x\\y\\z\\1\end{bmatrix}\]

  • y軸まわりの回転

\[\begin{bmatrix}x'\\y'\\z'\\1\end{bmatrix}=\begin{bmatrix}cos\theta&0&sin\theta&0\\0&1&0&0\\ -sin\theta&0&cos\theta&0\\0&0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\z\\1\end{bmatrix}=R_y(\theta)\begin{bmatrix}x\\y\\z\\1\end{bmatrix}\]

  • z軸まわりの回転

\[\begin{bmatrix}x'\\y'\\z'\\1\end{bmatrix}=\begin{bmatrix}cos\theta&-sin\theta&0&0\\sin\theta&cos\theta&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\z\\1\end{bmatrix}=R_z(\theta)\begin{bmatrix}x\\y\\z\\1\end{bmatrix}\]

拡大・縮小

\[\begin{bmatrix}x'\\y'\\z'\\1\end{bmatrix}=\begin{bmatrix}s_x&0&0&0\\0&s_y&0&0\\0&0&s_z&0\\0&0&0&1\end{bmatrix}\begin{bmatrix}x\\y\\z\\1\end{bmatrix}=S(s_x,s_y,s_z)\begin{bmatrix}x\\y\\z\\1\end{bmatrix}\]

これらは全て3次元アフィン変換です。

透視投影変換

Model変換、View変換は3次元同次座標変換で取り上げた変換行列の組み合わせによって成ります。Projection変換に関しては3次元アフィン変換以外に3次元射影変換(後述)が必要になります。今回は透視投影の場合のProjection変換について見ていきます。

f:id:es_program:20160426011103p:plain

視点から投影面までの距離を$d$とし、投影面の$x$方向の大きさを$2a$、$y$方向の大きさを$2b$とします。また、ビューボリューム(カメラの視錐台領域)のクリッピング領域の$z$値をそれぞれ$z_{min}$、$z_{max}$とします。

①. 正規化ビューボリュームは後方クリッピング面が$z=1$に、$z=1$のときのビューボリュームの$x,y$の範囲が$(-1 \leq x \leq 1),(-1 \leq y \leq 1)$の正方形になるようにビューボリュームを拡大・縮小したものです。ビューボリュームを正規化するには、前述したアフィン変換を用いてジオメトリに

\[ S(\frac{d}{az_{max}} ,\frac{d}{bz_{max}},\frac{1}{z_{max}}) \]

を掛け合わせます。

②. 正規化ビューボリュームを透視投影する(手前の物ほど大きく見え、奥の物ほど小さく見える)には、$\widetilde{z}_{min}=\frac{z_{min}}{z_{max}}$とおいて

\[ \begin{bmatrix}X'\\Y'\\Z'\\W'\end{bmatrix}=\ \begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0& \frac{1}{1-\widetilde{z}_{min}}&- \frac{\widetilde{z}_{min}}{1-\widetilde{z}_{min}}\\0&0&1&0\end{bmatrix}\ \begin{bmatrix}x\\y\\z\\1\end{bmatrix}=\ P(\widetilde{z}_{min})\begin{bmatrix}x\\y\\z\\1\end{bmatrix} \]

を掛け合わせます。$X',Y'$はもとの$x,y$から変化しないことに注目してください。また、$W'$は$z$の値がそのまま代入されることにも注目です。この変換で求められる座標は同次座標(変換行列$P(\widetilde{z}_{min})$の最下段に注目。アフィン変換ではなく射影変換。)であるため、通常座標に直すために求まった$X',Y',Z'$を$W'$で割る必要があります(この$W'$で割って通常座標を求めるという過程はProjection変換に含まれません)

頂点シェーダーの入出力と$w$

頂点シェーダにはローカル座標にある頂点がそのまま入ってきます(POSITION/SV_POSITIONセマンティクス)。スクリーンにポリゴンを投影させるためには、ローカル座標の頂点にModel変換、View変換、Projection変換(MVP行列)を掛け算します。そして、それを頂点シェーダの出力頂点として出力します。これでモデルはちゃんと画面に出ます(頂点シェーダーで明示的に$w$で割っていない!)。先に述べてきたように、ローカル座標にある頂点にMVP行列を掛け算すると、$w$で割る前の視錐台空間内の同次座標になります。つまり、頂点シェーダの出力頂点座標は、視錐台空間内の同次座標だったわけです。

頂点シェーダを抜けた頂点(POSITION/SV_POSITIONセマンティクスのついたもの)はそこで初めてGPUによって$w$で割り算され、スクリーンの投影位置が確定し、どの点を穿つべきか決められてピクセルシェーダに情報が伝わります。

参考

3次元射影変換

射影変換(ホモグラフィ)は平面を別の平面に射影することができる変換です。斜めから見たものを、もし正面から見たらどうなるかを計算できます。

3次元射影変換の一般形は

\[\begin{bmatrix}X'\\Y'\\Z'\\W'\end{bmatrix}=\begin{bmatrix}a&b&c&d\\e&f&g&h\\i&j&k&l\\m&n&o&p\end{bmatrix}\begin{bmatrix}X\\Y\\Z\\W\end{bmatrix}\]

です。