[Unity] シェーダを書く準備


どうやらUnityではやや特殊な仕組みがあるらしい。
それが[ShaderLab](http://docs.unity3d.com/Documentation/Components/SL-Shader.html)と呼ばれるもの。

これらの知識は[こちらの記事](http://marupeke296.com/UNI_S_No1_WhatIsShaderInUnity.html)を参考にしました。

------

## Unityで使えるシェーダの種類

WebGL（やOpenGL）などではシェーダを書き、頂点データやマトリクスなどはメインの言語で生成し、それをシェーダに送る、という仕組みで動作しています。
当然、固定パイプラインでなければライティングなど様々な表現を自分で書く必要があります。

しかしUnityの場合は、この **ShaderLab** が色々とめんどくさいところをやってくれているよう。
Unityで書けるシェーダにはいくつか種類があり、

1. Fixed Function Shaders（固定機能シェーダ）
2. Surface Shaders（サーフェイスシェーダ）
3. Vertex and Fragment Shaders（頂点・フラグメントシェーダ）

の3つ。
上から順に難易度というか、やるべきことが増えていきます。
メインで使うのは後半ふたつ。
そして（おそらく）一番書くであろう部分が真ん中の`Surface Shaders`になると思われます。


### 固定機能シェーダ

シェーダをサポートしていない端末の場合に利用されるもので、基本は使わないものらしいです。いわゆる代替手段。


### サーフェイス・シェーダ

Unityには通常のパイプラインの他に、ライティングパイプラインという別のフェーズがあるみたい。
詳しくは分からないけど、推測では通常のパイプラインの1フェーズとして存在し、その中でライティングやシェーディングを行う？
そしてその隙間に入れるシェーダが2番目の`Surface Shaders`ってことなんだと理解しました。


### 頂点・フラグメントシェーダ

こちらはいわゆる普通のシェーダ。
ライティングやシェーディングもすべて行える。柔軟だけど、上のライティングなども自分で実装しないとなので、その分やることが増えます。完全なプログラマブルシェーダですね。（当然と言えば当然）


## ShaderLab

**ShaderLab** はUnityとシェーダとの仲立ちをする部分。
これにも書式があって、ここにシェーダで利用する変数の定義や、UnityのGUIエディタ側に出す項目などを制御することができます。
また、SubShaderを複数定義でき、これは端末ごとに差異のあるシェーダを出し分ける仕組み。
つまり、色々な端末向けに作る場合は必要な分だけシェーダを書かないとならないことを意味しています。

*（ただ、通常はCg/HLSLで記述して、GLSLにトランスコードするのが基本らしいので、Unityでシェーダを書く場合はCg/HLSLで書くのがよさそう）*

### 書式

ごく簡単な書式は以下の感じ。

```ShaderLab
Shader "Path/To/ShaderName" {
	Properties {
		//...
	}
	SubShader {
		//...
	}
	SubShader {
		//...
	}
	SubShader {
		//...
	}
	//以下、必要なだけ繰り返し
	
	FallBack "Diffuse"
}
```

Shaderで囲まれた部分がひとつのシェーダを定義し、その中で、シェーダで使う変数やUnityのエディタに出す名前などを定義、関連付けと実際のシェーダコードを書いていきます。

### Properties
Propertiesはシェーダで使う変数とUnityエディタとの関連付けを行います。
ここで定義したものがシェーダ内で利用できる、というわけです。
使えるものは決まっていて、以下のような形で定義します。
Unityの公式サイトから引用すると以下のような定義になります。

>
Properties { Property [Property ...] }
Defines the property block. Inside braces multiple properties are defined as follows.

プロパティの定義ブロック。ブラケットの中は、以下のように複数のプロパティが定義されます。

>
name ("display name", Range (min, max)) = number
Defines a float property, represented as a slider from min to max in the inspector.

float型のプロパティ定義。minからmaxまでのスライダーをインスペクタに表示します。

>
name ("display name", Color) = (number,number,number,number)
Defines a color property.

カラープロパティ定義。

>
name ("display name", 2D) = "name" { options }
Defines a 2D texture property.

2Dテクスチャプロパティ定義。

>
name ("display name", Rect) = "name" { options }
Defines a rectangle (non power of 2) texture property.

矩形のプロパティ定義。

>
name ("display name", Cube) = "name" { options }
Defines a cubemap texture property.

キューブマップテクスチャのプロパティ定義。

>
name ("display name", Float) = number
Defines a float property.

float型のプロパティ定義。

>
name ("display name", Vector) = (number,number,number,number)
Defines a four component vector property.

4成分のベクトルプロパティの定義。

boolean風のプロパティ定義（チェックボックス）

>
[MaterialToggle] name ("display name", Float) = 0 // 0 is false, 1 is true

`name`はシェーダ内の変数名、`display name`はUnityのインスペクタに表示される名前です。
続く部分がいわゆる型になっていて、例えば`Vector`とするとUnityのインスペクタ側には4つの入力が表示される、という具合です。


### SubShader（サーフェイス・シェーダ）
サーフェイス・シェーダは冒頭で書いた通り、ライティングなどをUnity側でよしなにしてくれるタイプのシェーダです。

#### 書式

```SubShader
Shader "Custom/SampleShader" {
	Properties {
		_MainTex ("Base", 2D) = "white" {}
	}

	SubShader {
		Tags { "RenderType"="Transparent" }

		CGPROGRAM

		#pragma surface surf Lambert
		
		struct Input {
			float2 uv_MainTex;
			float4 vtxColor : COLOR;
		};

		void surf(Input IN, inout SurfaceOutput o) {
			half4 color = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = color.rgb;
		}

		ENDCG
	}
}
```


##### #pragma文

`#pragma`の行は、サーフェイスシェーダについての設定です。
`surface`というキーワードで始まり、次の`surf`は、シェーダのエントリ関数の名前を指定します。
*（その下に`surf`関数が定義されているのが分かるかと思います）*

最後の`Lambert`はランバート反射のことです。いくつかライティングについての設定ができるようです。
上記ではランバート反射を採用していることを通知しているわけですね。

ちなみにサンプルには書いていませんが、`Lambert`のあとにオプションを指定することもできるようです。


#### Tagsブロック

Tagsブロックは「いつ」「どのように」レンダリングを行うかを指定します。（いわゆる「Key-Value」型で）


#### Input構造体

以下のサンプルを見てください。

```ShaderLab
Shader "Custom/sample" {
	Properties {
		_MainColor("Color", Color) = (1, 1, 1)
		_MainTex("Texture", 2D) = "white" {}
	}
	SubShader {
		Tags { "RenderType"="Transparent" }
		
		CGPROGRAM
		
		float4 _MainColor;
		sampler2D _MainTex;
		
		#pragma surface surf Lambert alpha

		struct Input {
			float4 color : COLOR;
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb * _MainColor.rgb * half3(1, 0.5, 0.5);
			o.Alpha = 0.5;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}
```

中央付近に`Input`構造体が定義されているのが分かるかと思います。
ここは書式がいくらか決まっていて、Unity側からシェーダへデータのインプットをするための定義です。

上記サンプルでは、Unityのインスペクタからカラーとテクスチャをそれぞれ受け取るようになっています。
（しっかりドキュメント読んでいませんが、どうやら変数名のprefixに`_`は必須のようです（_MainTexなど））

インスペクタから受け取ったテクスチャ情報は、`Input`構造体で定義されている`uv_MainTex`メンバに引数として渡ってきます。
（どうやらこちらの`uv`プレフィクスも決まりがあるようです）


### 頂点シェーダ／フラグメントシェーダ

サーフェイスシェーダはライティングなどの基本的なことをさっと作るのには向いているシェーダ（だと思ってます）。
頂点シェーダ／フラグメントシェーダは、いわゆる普通のプログラマブルシェーダです。

以下のコードサンプルのように、`#pragma`文で頂点シェーダとフラグメントシェーダのエントリ関数名を指定します。

```ShaderLab
Shader "shaderName" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragument frag

			float4 vert(float4 v:POSITION) : SV_POSITION {
				return mul(UNITY_MATRIX_MVP, v);
			}

			fixed4 frag() : COLOR {
				return fixed4(1.0, 0.0, 0.0, 1.0);
			}

			ENDCG
		}
	}
}
```

ちなみに`UNITY_MATRIX_MVP`は、Unity側から渡される座標変換行列です。
`UNITY_MATRIX_M`、`UNITY_MATRIX_V`、`UNITY_MATRIX_P`、`UNITY_MATRIX_MV`など、それぞれ個別の行列や掛け合わされた行列が準備されているようです。
（組み込み値の種類はこちらを参照：[ShaderLab built-in values](http://docs.unity3d.com/Manual/SL-BuiltinValues.html)）

#### セマンティクス

上記のサンプルで、`:POSITION`や`:SV_POSITION`などの記述があります。
これはなにかというと、「頂点シェーダ入力（出力）セマンティクス」というものらしいです。
*（[セマンティクス詳細についてはこちら](http://msdn.microsoft.com/ja-jp/library/bb509647(v=vs.85).aspx)）*

*※ ちなみに`SV_`はおそらく`System Value（システム値）`の略。*

頂点シェーダでは通常、「頂点位置」以外にも「頂点色」や「頂点法線」など様々な入力があります。
そうした中で、`float4`（float型の4次元ベクトル）だけでは、どの情報なのか識別できません。
そこで、この「セマンティクス」を付与することで、「この変数は頂点位置だ」ということを意味付けしているようです。

ちなみに「頂点シェーダ出力セマンティクス」には以下のものが利用できるようです。

* 位置座標（POSIITON）
* ポイントサイズ（PSIZE）
* 頂点フォグ（FOG）
* 色（COLOR[n]：nはオプションの整数でCOLOR0などのように使う）
* テクスチャ座標（TEXCOORD[n]）（[n]はテクスチャ番号？）


#### ビルトイン シェーダ includeファイル / Built-in shader include files

Unityのサイト：[ビルトイン シェーダ includeファイル / Built-in shader include files](http://docs-jp.unity3d.com/Documentation/Components/SL-BuiltinIncludes.html)


Unityでは、いくつかの便利なヘルパー関数や、予め使える構造体などを定義したファイルを用意してくれているようです。

以下のようにすることで、ファイルを利用することが出来ます。

```ShaderLab
Shader "shaderName" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 3.0

			#include "UnityCG.cginc"

			float4 vert(appdata_base v) : POSITION {
				return mul(UNITY_MATRIX_MVP, v.vertex);
			}

			fixed4 frag(float4 sp:WPOS) : COLOR {
				fixed2 red_green = sp.xy / _ScreenParams.xy;
				fixed blue  = 0.0;
				fixed alpha = 1.0f;
				return fixed4(red_green, blue, alpha);
			}

			ENDCG
		}
	}
}
```

ちなみに、`appdata_base`は以下のような構造体です。

```ShaderLab
struct appdata_base {
	float4 vertex : POSITION;
	float3 normal : NORMAL;
	float4 texcoord : TEXCOORD0;
};
```

なお、セマンティクスがあるため、変数名は特に決まったルールはなさそうです。
*（`:POSITION`などを付けておけば自動的に適切に値が渡ってくる）*


#### インスペクタから値を設定する

Unityの優れている点は、外部から値を設定するのが簡単なところでしょう。
頂点シェーダ／フラグメントシェーダでも当然、外部から値を渡すことが可能です。

```ShaderLab
Shader "shaderName" {
	Properties {
		_RangeValue("Value", Range(0.0, 1.0)) = 0.2
		_MainTex("Texture", 2D) = "white" {}
	}
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			uniform float _RangeValue;
			uniform sampler2D _MainTex;
			
			// シェーダソース
			
			ENDCG
		}
	}
}
```

例のように、`Properties`でプロパティを定義し、シェーダ側では`uniform`修飾子を付けることで参照できるようになります。

色々と覚えることが多くて大変そうですが、なんとなくUnityでシェーダを書く準備が整った気がします。
今後はごりごりシェーダを書いてみたいですね。（まだまだ勉強しないとならないことがたくさんありますが・・）

ちなみに、本記事の大部分は[hecomiさんの記事](http://tips.hecomi.com/entry/2014/03/16/233943)を参考にさせていただきました。
上記記事では、Meshを使った、蝶が羽ばたくParticleを作成しています。一見の価値アリです。