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

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

- [Shader semantics](http://docs.unity3d.com/530/Documentation/Manual/SL-ShaderSemantics.html)
- [Providing vertex data to vertex programs](http://docs.unity3d.com/530/Documentation/Manual/SL-VertexProgramInputs.html)

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

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

```c:小さなvertexシェーダーの例
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](http://docs.unity3d.com/530/Documentation/Manual/SL-VertexFragmentShaderExamples.html)の"Environment Reflection with a Normal Map"）。

## 引数の指定の仕方
大きく分けて、セマンティクスを引数に直接記述するパターンと、引数の構造体を定義するパターンがある。
### 直接記述するパターン
```c:セマンティクスを引数に直接記述してvertexシェーダーへの入力を受け取る
float4 vert(float4 vertex : POSITION, float4 texcoord : TEXCOORD) : SV_POSITION {
    return mul(UNITY_MATRIX_MVP, vertex);
}
```

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

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

```c: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;
};
```

## 値の実際の使い方
```c:値の実際の使い方
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](http://docs.unity3d.com/530/Documentation/Manual/SL-ShaderSemantics.html)に書かれているその他のセマンティクスを簡単にまとめる。

| セマンティクス | 意味 | 主な型 | 利用箇所| 利用条件|
|:-:|:-:|:-:|:-:|:-:|
|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`を書く必要がある。