Unity でShaderの勉強 その1

ちょっとずつやっていきます。

###モデル紹介

モデルにはこちらを使用します。かわいい。
https://www.assetstore.unity3d.com/jp/#!/content/6808
<img width="820" alt="スクリーンショット 2016-09-19 3.15.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/21dec900-d1a0-d6a5-d2b5-a8498cbacab5.png">

デフォルト状態はこれ
<img width="283" alt="スクリーンショット 2016-09-19 3.11.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/0800a8cf-4516-9180-dce7-0839c674c63b.png">

###モデルデフォルトのマテリアルを調べる

デフォルトの状態でかわいいです。
デフォルトで設定されているマテリアル
<img width="300" alt="スクリーンショット 2016-09-19 3.16.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/c505c7e7-e620-8559-3c5a-700349b4aed4.png">

Unity付属のUnlit Transparent Cutout シェーダーが指定されていますね。
Unlit => Un lighting ライティングなし
Transparent => 透過あり
Cutout => アルファ情報が指定値以上の場合のみ透過する
Unlit系のシェーダーはUIに使うものだと思っていたけど、こういう使い方もできるのね。
デフォルメ系のモデリングだからテクスチャ自体に陰影をつけることでそれっぽく表現できる。

ただ、テクスチャを見てもアルファがないからUnit/Textureに変更しても見た目は変化しなかった。
Unit/Transparentに指定すると描画がおかしくなった。
<img width="200" alt="スクリーンショット 2016-09-19 3.28.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/f3152c9d-c2fe-ed1f-69a0-5a33158b3769.png">
うーん。なんでだろう。


###Standard shaderを指定してみる。
ライティング処理が行われ、ライトによる陰影がついた。
<img width="237" alt="スクリーンショット 2016-09-19 3.29.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/70cda5df-7ed3-3b9f-d358-a495d1247774.png">


###自作シェーダー
こんな感じのシェーダーを書いてみる

```lang:Test1
Shader "Custom/Test1" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			float4 vert(float4 v:POSITION) : SV_POSITION {
				return mul(UNITY_MATRIX_MVP,v); 
			}

			float4 frag() : COLOR {
				return fixed4(1,1,1,1);
			}
			ENDCG

		}

	}

}
```

結果はこんな感じ
<img width="190" alt="スクリーンショット 2016-09-19 3.43.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/72e84b67-71c4-823f-db2f-a1dae9644236.png">
frag関数でColor.whiteを返してるから真っ白に描画される。
ライティングなしなのでアルファ部分を0にしても真っ白に描画される。


###pragmaってなに
pragma(プラグマ)
コンパイラに指示を与えるやつ

```
#pragma vertex vert
```

でvertexを処理する関数がvertという名前の関数であることをコンパイラに指示している。

###SubShaderを二個にしてみた

```lang:SubShaderを二個
Shader "Custom/Test1" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			float4 vert(float4 v:POSITION) : SV_POSITION {
				return mul(UNITY_MATRIX_MVP,v); 
			}

			float4 frag() : COLOR {
				return fixed4(1,0,0,1);//赤
			}
			ENDCG

		}

	}

	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			float4 vert(float4 v:POSITION) : SV_POSITION {
				return mul(UNITY_MATRIX_MVP,v);
			}

			float4 frag() : COLOR {
				return fixed4(0,1,0,1);//緑
			}
			ENDCG
		}
	}

}
```

<img width="205" alt="スクリーンショット 2016-09-19 3.50.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/01ffc66e-f80b-d566-810c-9e2480867a39.png">

１つ目のSubShaderが実行されて赤色が描画された。

###Passを増やす

```lang:Pass増やした
Shader "Custom/Test1" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			float4 vert(float4 v:POSITION) : SV_POSITION {
				return mul(UNITY_MATRIX_MVP,v); 
			}

			float4 frag() : COLOR {
				return fixed4(1,0,0,1);//赤
			}
			ENDCG

		}
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			float4 vert(float4 v:POSITION) : SV_POSITION {
				return mul(UNITY_MATRIX_MVP,v);
			}

			float4 frag() : COLOR {
				return fixed4(0,1,0,1);//緑
			}
			ENDCG
		}
	}

}
```

<img width="201" alt="スクリーンショット 2016-09-19 3.51.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/3c654115-efe8-7b33-3e54-b35b9157ada6.png">

一度赤色が描画された後、緑色が描画された。

###vertexをいじってみた

```lang:modify_vertex
Shader "Custom/Test1" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			float4 vert(float4 v:POSITION) : SV_POSITION {
//				return mul(UNITY_MATRIX_MVP,v); 
				return v;
			}

			float4 frag() : COLOR {
				return fixed4(1,0,0,1);//赤
			}
			ENDCG

		}
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			float4 vert(float4 v:POSITION) : SV_POSITION {
				return mul(UNITY_MATRIX_MVP,v);
			}

			float4 frag() : COLOR {
				return fixed4(0,1,0,1);//緑
			}
			ENDCG
		}
	}

}
```

<img width="575" alt="スクリーンショット 2016-09-19 3.53.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/61a046a0-01bf-0314-16c4-790a7bf7d45b.png">

１つ目のPassであえてMVP変換しないでfragmentに処理を回すとこうなるのね。
Pass間で情報を共有したい場合はどうすれば良いんだろう...


###vertexの引数を変える(引数を増やす)
vertexの引数は任意に変えることができます。
１つだけのパラメータを引数で受け取ったり、複数のパラメータを受け取ったりできます。
普通の関数のようにカンマ区切りで欲しい引数を定義しましょう。

```
Shader "Custom/Test1" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			float4 vert(float4 pos : POSITION , float4 uv : TEXCOORD0) : POSITION {
				return mul(UNITY_MATRIX_MVP, pos);
			}

            float4 frag() : COLOR {
                return fixed4(1,0,0,1);//赤
            }

			ENDCG
		}
	}

}
```


###vertexの引数を変える(struct定義)
POSITIONのパラメータ(頂点のPOSITION)をstruct経由で受け取ってみましょう。

```
Shader "Custom/Test1" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			struct appdata {
				float4 pos : POSITION;
			};

			float4 vert(appdata data) : POSITION {
				return mul(UNITY_MATRIX_MVP, data.pos);
			}

            float4 frag() : COLOR {
                return fixed4(1,0,0,1);//赤
            }

			ENDCG
		}
	}

}
```

ポイント1
structで必要なパラメータを定義しています。semanticsが必要なので注意

```
			struct appdata {
				float4 pos : POSITION;
			};
```

ポイント2
vert関数の引数を変更します

```
float4 vert(appdata data) : POSITION {
```

これでdata.posで頂点データにアクセスできるようになりました。

vert関数の戻り値の型はfloat4でsemanticsはPOSITIONになっています。
これもvertの引数のように自由に変えることができます。(ただしPOSITIONの情報を返すのはほぼ必須です)
semantics関連の説明はこちら
https://docs.unity3d.com/ja/current/Manual/SL-ShaderSemantics.html

###vert関数でPOSITIONを返さない

```
Shader "Custom/Test1" {
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			struct v2f {
				float4 uv : TEXCOORD0;
			};

			v2f vert(float4 pos: POSITION,out float4 outputs : POSITION)  {
				v2f o;
				outputs = mul(UNITY_MATRIX_MVP , pos);
				return o;
			}

			float4 frag() : COLOR {
				return float4(1,0,0,1);
			}

			ENDCG
		}
	}

}
```

ポイント
out修飾子をつける。

```
v2f vert(float4 pos: POSITION,out float4 outputs : POSITION)  {
```

最大で1つしかout修飾子をつけれないので注意。

out修飾子をつけた引数の方が前に来ても良い。

```
v2f vert(out float4 outputs : POSITION , float4 pos: POSITION)  {
```

これはだめ。out修飾子をつけられた引数は取得できない。代入だけ

```
v2f vert(out float4 pos: POSITION)  {
    v2f o;
    pos = mul(UNITY_MATRIX_MVP , pos);
```

vert関数ではvertex(float4のPOSITION)さえ加工できれば良いため、返り値なしでもコンパイルは通る。
なのでこう書いても良い。

```
void vert(out float4 outputs : POSITION , float4 pos: POSITION)  {
	outputs = mul(UNITY_MATRIX_MVP , pos);
}
```



###Texture貼りたい
プロパティを増やしましょう。

```lang:プロパティ増やした
Shader "Custom/Test1" {
	Properties {
		_MainTex("main Texture" , 2D) = "white" {}
	}

	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag


			uniform sampler2D _MainTex;

			struct v2f {
				float4 uv : TEXCOORD0;
				float4 wpos : SV_POSITION;
			};

			v2f vert(float4 pos: POSITION, float4 uv : TEXCOORD0)  {
				v2f o;
				o.wpos = mul(UNITY_MATRIX_MVP , pos);
				o.uv = uv;
				return o;
			}

			float4 frag(v2f i) : COLOR {
				float4 tex = tex2D(_MainTex,i.uv);
				return tex;
			}

			ENDCG
		}
	}

}
```

vert関数でその頂点のuvとposを含むv2fを返し、
frag関数でv2fを受け取っている。
tex2D関数でuvからtextureの色を抜き出して戻り値にしている。

マテリアルにTextureを指定してあげることでデフォルトのやつと同じようになった。
<img width="161" alt="スクリーンショット 2016-09-19 13.48.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/d19780ef-75d1-d534-82bb-781665479fe4.png">

uniformってなんだろう。

```lang:uniformって・・・
uniform sampler2D _MainTex;
```


###uniformって何?
- uniformはvert関数/frag関数で自由に使える変数。
- uniformはPropertiesで定義した変数をvert関数/frag関数でアクセスできるようにするやつ。
- uniformで定義することでスクリプトから変数を制御することができるようになる。
- 頂点やピクセル毎の変数ではなく、マテリアル毎の変数となる。


###テクスチャを重ねてみる

```lang:mainTex_and_subTex
Shader "Custom/Test1" {
	Properties {
		_MainTex("main" , 2D) = "white" {}
		_SubTex("sub",2D) = "white" {}
	}
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			uniform sampler2D _MainTex;
			uniform sampler2D _SubTex;

			float4 vert(out float4 wpos : POSITION , float4 pos : POSITION , float4 uv : TEXCOORD0) : TEXCOORD0 {
				wpos = mul(UNITY_MATRIX_MVP,pos);
				return uv;
			}

			float4 frag(float4 uv : TEXCOORD0) : COLOR {
				float4 mainTex	= tex2D(_MainTex,uv);
				float4 subTex	= tex2D(_SubTex,uv);
				return mainTex * subTex;
			}

			ENDCG
		}
	}
}
```

プレビュー
<img width="170" alt="スクリーンショット 2016-09-19 14.56.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/f1282f0d-a968-c020-7b4a-b6ffa74fbf8f.png">

<img width="282" alt="スクリーンショット 2016-09-19 14.56.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/90cf6955-510c-a322-ddcf-890f3c53e0b0.png">

###マテリアルのTilingとOffsetを反映させる
Tiling => タイリング ? タイル ? => 何枚敷くか
Offset => オフセット => どれくらいずらすか

普通にtex2D関数だけでtextureの色情報を持ってきただけではtilingやoffsetが反映されません。

```lang:tilingやoffsetが効かない
				float4 mainTex	= tex2D(_MainTex,uv);
				float4 subTex	= tex2D(_SubTex,uv);
```

StanderdShaderでは、マテリアルのtilingやoffsetを操作すると、モデルのuvが加工されています。

```lang:tiling_and_offset_enabled
Shader "Custom/Test1" {
	Properties {
		_MainTex("main" , 2D) = "white" {}
		_SubTex("sub",2D) = "white" {}
	}
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			uniform sampler2D _MainTex;
			uniform sampler2D _SubTex;
			uniform float4 _MainTex_ST;

			float2 vert(out float4 wpos : POSITION , float4 pos : POSITION , float2 uv : TEXCOORD0) : TEXCOORD0 {
				wpos = mul(UNITY_MATRIX_MVP,pos);
				float2 tiling_offset_uv = float2(uv.xy * _MainTex_ST.xy + _MainTex_ST.zw);
				return tiling_offset_uv;
			}

			float4 frag(float2 uv : TEXCOORD0) : COLOR {
				float4 mainTex	= tex2D(_MainTex,uv);
				float4 subTex	= tex2D(_SubTex,uv);
				return mainTex * subTex;
			}

			ENDCG
		}
	}
}
```

ポイント
テクスチャの名前 + _STをつけたfloat4のuniformを定義することでそのTextureのtilingやoffset情報を取ってくることができます。一見何も代入されていないように見えますが、変数の中身はunity側によって事前に代入されます。

```lang:_ST
uniform float4 _MainTex_ST;
```

_MainTex_STを使ってUVを加工します。uv加工の式はお決まりの定型文です。

```lang:uvを加工
float2 tiling_offset_uv = float2(uv.xy * _MainTex_ST.xy + _MainTex_ST.zw);
                return tiling_offset_uv;
```


またCGPROGRAM内に#include "UnityCG.cginc"を書くことでunity組み込みのuv加工関数を使えるようになります。

```lang:TRANSFORM_TEX関数を使ってみる
				//float2 tiling_offset_uv = float2(uv.xy * _MainTex_ST.xy + _MainTex_ST.zw);
				float2 tiling_offset_uv = TRANSFORM_TEX(uv,_MainTex);
				return tiling_offset_uv;
```

###SubTextureもTiling対応
こんな感じでいいのかな？
TRANSFORM_TEX関数を使うとうまく対応できなかった・・・

```
				float2 tiling_offset_uv = float2(uv.xy * _MainTex_ST.xy * _SubTex_ST + _MainTex_ST.zw + _SubTex_ST.zw);
//				float2 tiling_offset_uv = TRANSFORM_TEX(uv,_MainTex);
				return tiling_offset_uv;
```

###Colorを使ってみる

```lang:プロパティにカラーを追加
Shader "Custom/Test1" {
	Properties {
		_MainTex("main" , 2D) = "white" {}
		_Color ("color" , Color) = (1,1,1,1)

	}
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			uniform sampler2D _MainTex;
			uniform float4 _Color;		

			float2 vert(out float4 wpos : POSITION , float4 pos : POSITION , float2 uv : TEXCOORD0) : TEXCOORD0 {
				wpos = mul(UNITY_MATRIX_MVP,pos);
				return uv;
			}

			float4 frag(float2 uv : TEXCOORD0) : COLOR {
				float4 mainTex	= tex2D(_MainTex,uv);

				return mainTex * _Color;
			}

			ENDCG
		}
	}
}
```

###時間経過によって色を変える
_SinTimeを使って時間経過を取得し、それを使ってColor値を変えます。
ゲームを再生しないと_SinTimeの値が変化しないので注意。

```
Shader "Custom/Test1" {
	Properties {
		_MainTex("main" , 2D) = "white" {}
		_Color ("color" , Color) = (1,1,1,1)

	}
	SubShader {
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			uniform sampler2D _MainTex;
			uniform float4 _Color;		

			float2 vert(out float4 wpos : POSITION , float4 pos : POSITION , float2 uv : TEXCOORD0) : TEXCOORD0 {
				wpos = mul(UNITY_MATRIX_MVP,pos);
				return uv;
			}

			float4 frag(float2 uv : TEXCOORD0) : COLOR {
				float4 mainTex	= tex2D(_MainTex,uv);
				_Color.x = (_SinTime.w + 1.0 ) * 0.5;
				return mainTex * _Color;
			}

			ENDCG
		}
	}
}
```

思ったより_SinTimeが遅かったので早くしてみる。
二倍角の公式を使う。

```lang:点滅を速く
			float4 frag(float2 uv : TEXCOORD0) : COLOR {
				float4 mainTex	= tex2D(_MainTex,uv);
				//2倍速
				//_Color.x = (2.0 *_SinTime.w * _CosTime .w);
				//3倍速
				_Color.x = (3.0 * _SinTime.w - 4.0 * _SinTime.w * _SinTime.w *_SinTime.w);
				return mainTex * _Color;
			}
```

これでもちょっと遅いな・・・もっと爆速で点滅して欲しい。


###_SinTimeじゃなくて_Timeを使う
これで任意のスピードでチカチカしてくれる。

```lang:_Time
				float speed = 10;
				//10倍じゃ！
				_Color.x = (sin(_Time.w * speed) + 1.0) * 0.5;
				return mainTex * _Color;
```


###Semantics SV_POSITIONとPOSITIONの違い
こちらによると
https://msdn.microsoft.com/en-us/library/windows/desktop/bb509647.aspx

> Migration from Direct3D 9 to Direct3D 10 and later
The following issues should be considered when migrating code from Direct3D 9 to Direct3D 10 and later:
Mapping to Direct3D 9 Semantics
A few of the Direct3D 10 and later semantics map directly to Direct3D 9 semantics.
Direct3D 10 Semantic	Direct3D 9 Equivalent Semantic
SV_Depth	DEPTH
SV_Position	POSITION
SV_Target	COLOR
 
> Note to Direct3D 9 developers:
For Direct3D 9 targets, shader semantics must map to valid Direct3D 9 semantics. For backwards compatibility POSITION0 (and its variant names) is treated as SV_Position, COLOR is treated as SV_TARGET.

Direct3D 9 だと DEPTHやPOSITIONやCOLORといったSemanticsが使えたけど、
Direct2D 10 から SV_DepthやSV_PositionやSV_Targetに変わった。
"SV_"を接頭につけることで10に対応することができるってことかな。

積極的にSV_系のSemanticsを使っていきたい。