CGエンジニア検定の勉強に伴って,「コンピュータグラフィックス」という教科書を読み始めているため,学習したことをUnityや現在勉強中のシェーダ言語と結び付けてまとめてみようと思います。
UnityのShaderLab言語の理解と,CGエンジニア検定の対策を兼ねることを目的としています。
Shaderの基本的な構造
struct appdata
(
float4 vertex : POSITION;
)
struct v2f
(
float4 vertex : SV_POSITION;
)
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return 0;
}
これが頂点シェーダ部分の基本的構造になっています。
POSITION,SV_POSITIONなどはセマンティクスと呼ばれ,プログラム内では変数名は自由に設定できますが,その変数がレンダリングパイプラインにおいてどの役割をするかはコンパイラは理解することができません。
そこで,これはこういう役割ですよ!と
変数 = "ある役割"
であることを明示するために書くもののことがセマンティクスとなります。これは適宜覚える必要があります。
UnityObjectToClipPos()とは
上記のコードの
o.vertex = UnityObjectToClipPos(v.vertex);
のなかのUnityObjectToClipPos関数について解説します。以下はCGエンジニア検定にて出題される可能性のある内容です。
v.vertexとして関数の中に入力される変数は3次元空間の頂点情報で,
(float4なので4変数関数です。)
これをこの関数に入れることで,カメラが映す3次元空間に射影変換します。
モデルビュー行列とは
3次元空間にある情報をすべてカメラの2次元空間への情報へと変換しようとすると,変換させる情報量が多すぎるので,実際はビューボリュームという制限された範囲の3次元空間の情報に限定して,それを2次元に変換して描画するという方法がとられます。
Unity Shader Programming Vol.01では
UnityObjectToClipPos関数は,座標 * モデルビュー行列 * 射影行列 に等しいです。
とありますが,ここでの 座標 * モデルビュー行列(モデリング変換とビューイング変換) によって,描画範囲を限定して,空間をz軸で180度回転させる変換を行う。(「コンピュータグラフィックス」Chapter2-4-1)
射影変換・投影変換・ビューポート変換
(射影変換は3次元アフィン変換とは違うことに注意)
射影変換と書いていますが,基本的には投影変換とよばれる変換が行われます。(投影変換は射影変換のひとつです)
投影変換では正規化ビューボリュームとよばれる[-1,1] * [-1,1] * [0,1]の3次元空間に変換されます。この変換で得られる奥行きの情報(深度情報?)が陰面消去などに用いられます。
正規化ビューボリュームの求め方
\left(\begin{matrix}
x'\\
y'\\
z'\\
w'
\end{matrix}
\right)=
\left(\begin{matrix}
1&0&0&0\\
0& 1 & 0 & 0\\
0 & 0 & \frac{1}{1 - z'_{min}} & - \frac{z'_{min}}{1 - z'_{min}}\\
0 & 0 & 1 & 0
\end{matrix}
\right)\times
\left(\begin{matrix}
x\\y\\z\\1
\end{matrix}
\right)
ただし
z'_{min} \equiv \frac{z_{min}}{z_{max}}
と,教科書には書いてあるのですが!(33)(34)の項がなぜそうなるの?とよくわからないままでしたが,以下のように求めればいいことがわかりました。
\left(\begin{matrix}
x'\\
y'\\
z'\\
w'
\end{matrix}
\right)=
\left(\begin{matrix}
1&0&0&0\\
0& 1 & 0 & 0\\
0 & 0 & \alpha & \beta \\
0 & 0 & 1 & 0
\end{matrix}
\right)\times
\left(\begin{matrix}
x\\y\\z\\1
\end{matrix}
\right)
のようにして,α,βをおき,
\frac{x'}{w'} = \frac{x}{z}\\
\frac{y'}{w'} = \frac{y}{z}
をみたすように,この2元連立方程式を2つの未知数に対してとけば,うえのように求めることができるというわけです。詳しく計算してないです...()
UnityObjectToClipPos関数ではここまでの投影変換までを行って,カメラのウィンドウの座標に受け渡して,Fragment関数へ渡せるように変換している?(と捉えています。)
ここでのo.vertexは,投影変換後の正規化された3次元空間の座標を表していると考えているのですが,実際の変換コードを調べたほうがはやそう...
ビューポート変換
念のため説明します。ここまでの投影変換による投影座標系では,[-1,1] * [-1,1]の範囲が画面に表示されるウィンドウに該当します。これを,実際に表示される1024*1024などの画面の範囲内に切り取って表示する変換をビューポート変換といいます。
Shaderでの利用での利点
v2f vert()のところで変換されたo.vertexは3次元空間で正規化された座標なので,そのままそれぞれ[0,1]の範囲のSV_TARGETとして返すことができるので,利用しやすい?
疑問
- 4成分目のwの役割は変換における定数項の表現のための便宜的なものなのか
- クリッピングの方法の詳細
参考
- Unity Shader Programming Vol.01
- Unity シェーダープログラミングの教科書 ~ShaderLab言語解説編~
- http://www.az.cs.is.nagoya-u.ac.jp/class/comp-sys/cg_chap_5.pdf
- https://qiita.com/edo_m18/items/946df01d58b8cd3143d4