Unityのシェーダーセマンティクスまとめ

  • 37
    いいね
  • 0
    コメント

この記事は、主に以下のUnityのドキュメントを自分なりに解釈してまとめたものです。

Unityのバージョンは5.3系です。
vertexシェーダーとfragmentシェーダーの入出力に関わるセマンティクスに限定しています。
サンプルプログラムはShaderLabの記述あたりは省略しています。

概要

セマンティクスとは、シェーダープログラムの入力や出力の値が、何を意味するかを表すためのもの。
型が、値の表現の幅(最大値は何か、実数か小数か、スカラーかベクトルか、…)を表すのに対して、セマンティクスは、値の用途(位置か、法線か、…)を表すイメージ。

小さなvertexシェーダーの例
float4 vert(float4 v : POSITION) : SV_POSITION {
    return mul(UNITY_MATRIX_MVP, v);
}

↑の例では、POSITIONSV_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")。

引数の指定の仕方

大きく分けて、セマンティクスを引数に直接記述するパターンと、引数の構造体を定義するパターンがある。

直接記述するパターン

セマンティクスを引数に直接記述してvertexシェーダーへの入力を受け取る
float4 vert(float4 vertex : POSITION, float4 texcoord : TEXCOORD) : SV_POSITION {
    return mul(UNITY_MATRIX_MVP, vertex);
}

構造体を定義するパターン

構造体を定義してvertexシェーダーへの入力を受け取る
struct appdata {
    float4 vertex : POSITION;
    float4 texcoord : TEXCOORD0;
};
float4 vert(appdata v) : SV_POSITION {
    return mul(UNITY_MATRIX_MVP, v.vertex);
}

よく使われそうな構造体はUnityCG.cgincで予め定義されているので、それを使うことも可能。

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を書く必要がある。