この記事は、主に以下のUnityのドキュメントを自分なりに解釈してまとめたものです。
Unityのバージョンは5.3系です。
vertexシェーダーとfragmentシェーダーの入出力に関わるセマンティクスに限定しています。
サンプルプログラムはShaderLabの記述あたりは省略しています。
概要
セマンティクスとは、シェーダープログラムの入力や出力の値が、何を意味するかを表すためのもの。
型が、値の表現の幅(最大値は何か、実数か小数か、スカラーかベクトルか、…)を表すのに対して、セマンティクスは、値の用途(位置か、法線か、…)を表すイメージ。
float4 vert(float4 v : POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP, v);
}
↑の例では、POSITION
、SV_POSITION
がセマンティクスである。それぞれ、型のfloat4
とは別に付加されているのが分かる。
vertexシェーダーへの入力
vertexシェーダーの入力(関数の引数)は全てセマンティクスを持たねばならない。
セマンティクス一覧
セマンティクス | 意味 | 主な型 |
---|---|---|
POSITION | 頂点の位置 | float3, float4 |
NORMAL | 頂点の法線 | float3 |
TEXCOORD0 | 1番目のUV座標 | float2, float3, float4 |
TEXCOORD1, TEXCOORD2, TEXCOORD3 | 2,3,4番目のUV座標 | TEXCOORD0と同じ |
TANGENT | 接線 | float4 |
COLOR | 頂点の色 | float4 |
メッシュデータの次元が求めてるものより小さい場合、0埋めされる。ただし、wには1が入る。 | ||
テクスチャ座標は基本2次元なので、float4が指定されると(x,y,0,1)となる。 | ||
TANGENTのw値はbitangent(従接線)の方向を示すのに用いられる。 | ||
バンプマッピングとかしたいときにはこのTANGENTが役に立つはず(cf. Vertex and Fragment Shader Examplesの"Environment Reflection with a Normal Map")。 |
引数の指定の仕方
大きく分けて、セマンティクスを引数に直接記述するパターンと、引数の構造体を定義するパターンがある。
直接記述するパターン
float4 vert(float4 vertex : POSITION, float4 texcoord : TEXCOORD) : SV_POSITION {
return mul(UNITY_MATRIX_MVP, vertex);
}
構造体を定義するパターン
struct appdata {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
float4 vert(appdata v) : SV_POSITION {
return mul(UNITY_MATRIX_MVP, v.vertex);
}
よく使われそうな構造体はUnityCG.cgincで予め定義されているので、それを使うことも可能。
struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct appdata_tan {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct appdata_full {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
#if defined(SHADER_API_XBOX360)
half4 texcoord4 : TEXCOORD4;
half4 texcoord5 : TEXCOORD5;
#endif
fixed4 color : COLOR;
};
struct appdata_img
{
float4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
};
値の実際の使い方
float4 vert(appdata_base v) : SV_POSITION {
float4 clipPos = mul(UNITY_MATRIX_MVP, v.vertex); // 頂点のクリップ座標
float2 wNormal = normalize(UnityObjectToWorldNormal(v.normal)); // world空間における正規化された法線
// 略
}
入力は、メッシュでの値なのでクリップ空間での座標や、world空間での法線を求めるためには各種変換を行う必要がある。
ちなみに、normalとかtangentとかは正規化して使うのが無難そう。
fragmentシェーダーの出力
セマンティクス一覧
セマンティクス | 意味 | 主な型 |
---|---|---|
SV_TARGET | ピクセルの色 | fixed4 |
SV_TARGET1,SV_TARGET2,... | 他のレンダリング対象に対する色 | fixed4 |
SV_DEPTH | ピクセルの深度 | float |
SV_TARGET1,...は、一度に複数の対象(render target)にレンダリングするときに使う(マルチターゲットレンダリングと呼ばれる技術)。 | ||
深度は基本的に通常のラスタライズ処理で勝手に決まるので、SV_DEPTHを使ってそれを上書きするのはあまりやらないほうが良いらしい。 |
vertexシェーダーの出力、fragmentシェーダーへの入力
セマンティクス一覧
セマンティクス | 意味 | 主な型 |
---|---|---|
SV_POSITION | 頂点のクリップ座標(必須) | float4(必ず) |
TEXCOORD0,TEXCOORD1,... | テクスチャ座標や位置や方向など | float2, float3, half3 |
COLOR0,COLOR1,... | 色など | fixed4 |
SV_POSITIONはGPUがラスタライズ処理を行うために必ず必要となる。
他の値は、vertexシェーダーによって出力された値が、レンダリングされる三角形面に応じて補間されることにより、fragmentシェーダーに渡される。
TEXCOORD0やCOLOR0などには、古いGPUでは変数の中身の解釈に違いが合ったが、今は特に関係ないらしい。
補間される変数の数の限界
補間される変数の数の限界は、プラットフォームとGPUによって制限される。
ガイドラインは以下のとおり。
- 8個まで : OpenGL ES 2.0 (iOS/Android), Direct3D 11 9.x level (Windows Phone) and Direct3 9 shader model 2.0 (old PCs).
- 10個まで : Direct3D 9 shader model 3.0 (#pragma target 3.0).
- 16個まで : OpenGL ES 3.0 (iOS/Android), Metal (iOS).
- 32個まで : Direct3D 10 shader model 4.0 (#pragma target 4.0).
取り敢えず9個以上使いたくなったら気をつけたほうが良さそう。
その他
Shader semanticsに書かれているその他のセマンティクスを簡単にまとめる。
セマンティクス | 意味 | 主な型 | 利用箇所 | 利用条件 |
---|---|---|---|---|
VPOS | スクリーン空間における座標 | UNITY_VPOS_TYPE(基本float4) | fragmentシェーダーへの入力 | shader model 3.0 |
VFACE | 面がカメラを向いているかどうか(向いていたら正、そうでなければ負) | fixed | fragmentシェーダーへの入力 | shader model 3.0 |
SV_VertexID | 頂点番号 | uint | vertexシェーダーへの入力 | DX10(shader model 4.0), GLCore / OpenGL ES 3 |
VPOS, VFACEは#pragma target 3.0
、SV_VertexIDは#pragma target es3.0
を書く必要がある。