ユニティちゃんで学習。座標を色に変換するUnity 3Dシェーダーの作り方。
スクリーン座標の分かり辛いz軸とw軸について。
関連ページ
参考URL
前回
はマテリアルに単色を代入するシェーダーを作りました。
今回はモデルの座標を無理やり色に変換するシェーダーを作っていきます。

テストに使う3Dモデル
3Dモデルは
ユニティちゃんPackege
と
UnityToonShader
を使っていきます。

スクリーン座標を色に変換するシェーダー
頂点シェーダーで受け取った座標を、ピクセルシェーダーで色に変換するコードを書いていきます。
実用的ではないですが、シェーダーの仕組みを知るのに割と便利です。
以下コード全文です。
Shader
"Unlit/CoordinatesColor"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma
vertex
vert
#pragma
fragment
frag
//後述のComputeScreenPos関数に必要な宣言
#include
"UnityCG.cginc"
struct
appdata
{
float4
vertex
:
POSITION;
};
struct
v2f
{
float4
vertex
:
SV_POSITION;
//スクリーン座標を保存してピクセルシェーダーに伝えるために追加
float4
screenPos
:
TEXCOORD0;
};
// 頂点シェーダー
v2f
vert
(appdata
v)
{
v2f
o;
// ワールド座標をカメラを原点とする座標系に変換し、
// かつカメラの画角外の頂点を削ります
o.vertex
=
UnityObjectToClipPos(v.vertex);
// クリップ座標を正規化してスクリーン座標に変換
o.screenPos
=
ComputeScreenPos(o.vertex);
return
o;
}
// ピクセルシェーダー
fixed4
frag
(v2f
i)
:
SV_Target
{
//座標情報を無理やり色に変換
//x軸をr(赤)に、y軸をg(緑)、z軸をb(青)に代入する
return
fixed4(i.screenPos.x,
i.screenPos.y,
i.screenPos.z,
1);
}
ENDCG
}
}
}
後述しますが、i.screenPos.zの代入だけは実際はz軸の座標が関係ない処理になっています。
ユニティちゃんの服に使っているdef_matのシェーダーを、作成したシェーダーに変更します。
実装した結果は次のようなものになります。

スクリーン座標とは何か
Unityシェーダーで言うスクリーン座標とは、UnityC#でいうビューポート座標に近いです。
カメラの画角の左下をx=0, y=0として、右上をx=1, y=1とする、正規化された座標の事を言います。
下の画像を見ての通り、x軸が右に寄るほど赤色が濃くなり、y軸が上に寄るほど緑色が強く出ています。
一方でビューポート座標とは手前と奥の座標の扱い方がかなり異なります。
ComputeScreenPosで得られたスクリーン座標は、z軸が常に0.5で意味のない値になっています。
z軸の代わりに使うのがw軸の座標です。
スクリーン座標のw軸は、カメラの座標に近づくほど0に近づき、カメラから離れるほど1に近づきます。
カメラのz軸座標と完全に重なったとき、このw軸は0になります。
またTransformのz軸の値が、カメラのz軸から+1になった段階で、このw軸は1になります。そこから1以上には増えません。
つまり先ほどピクセルシェーダー内のコードは、スクリーン座標のz軸を青色に代入しているので、実際には青色の値は常に0.5になっています。
// ピクセルシェーダー
fixed4
frag
(v2f
i)
:
SV_Target
{
//座標情報を無理やり色に変換
//x軸をr(赤)に、y軸をg(緑)、z軸をb(青)に代入する
return
fixed4(i.screenPos.x,
i.screenPos.y,
i.screenPos.z,
1);
}
分かりやすく四角形で座標と色の関係をまとめると次の通りです。

コードを少し解説
最初に、使いたい関数を使うのに必要なコンポーネントをincludeしています。
C#でいうusingと同じような処理です。
//後述のComputeScreenPos関数に必要な宣言
#include
"UnityCG.cginc"
出力頂点構造体の方にのみ、ピクセルシェーダーに渡す変数を追加しています。
//スクリーン座標を保存してピクセルシェーダーに伝えるために追加
float4
screenPos
:
TEXCOORD0;
この変数には頂点シェーダー内でスクリーン座標を代入します。
下がその、実際にスクリーン座標を代入している処理です。
// クリップ座標を正規化してスクリーン座標に変換
o.screenPos
=
ComputeScreenPos(o.vertex);
最後にスクリーン座標を無理やり色に変換して完成。
//座標情報を無理やり色に変換
//x軸をr(赤)に、y軸をg(緑)、z軸をb(青)に代入する
return
fixed4(i.screenPos.x,
i.screenPos.y,
i.screenPos.z,
1);
0
0