Unity Shaderの基礎 part 1

# はじめに
自分の為にUnityのシェーダの基礎から勉強をしていきます。

Unityのシェーダはざっくり分けると以下の３種類があります。

- サーフェイスシェーダ
- 頂点/フラグメントシェーダ
- 固定関数シェーダ

サーフェイスシェーダは、ライティング等を自動で行ってくれます。
頂点/フラグメントシェーダは、ライティングを使わない場合などに使います。
ライティングも可能ですが、全部自力で書く必要があるので、自由度は高いですがその分難易度も高いです。
固定関数シェーダは、プログラマブルシェーダに対応していない古いハードウェア用のものなので、今回は勉強しない方向です。

---
# 簡単なサーフェイスシェーダ

いきなりですが、サーフェイスシェーダのサンプルです。

```
Shader "Custom/surfaceshader001" {
	SubShader {
		Tags { "RenderType"="Opaque" }

		CGPROGRAM
		#pragma surface surf Lambert

		struct Input {
			float4 color : COLOR;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			o.Albedo = half3(0.75, 1.0, 0.5);
		}
		ENDCG
	}
	FallBack "Diffuse"
}
```
## 説明

上から順に説明します。

---

```
Shader "Custom/surfaceshader001"
```
シェーダ自身の名前です。
Shader "グループ名/シェーダー名" という感じです。

---

```
SubShader {
　～～
}
```
シェーダの実装部分です。
ちなみに、１つのシェーダ内にSubShader{}を複数記述することもできます。
その場合は、上から順に実行可能なSubShaderが選択されます。
いろんなハードで動作させるために書き方を変えたりするためです。

---

```
Tags { "RenderType"="Opaque" }
```

Tagsは、SubShaderが「いつ」「どのように」レンダリングされるかの情報です。
この例では、**RenderType**を**Opaque（不透明）**に指定しています。
タグは**RenderType**以外にも色々ありますが、おいおい紹介していきます。

---

```
CGPROGRAM
　～～
ENDCG
```

サーフェイスシェーダでは、実際の描画処理を**CGPROGRAM**～**ENDCG**の間に記述します（パスと呼ぶ）
また、一つのSubShaderの中に複数の**CGPROGRAM**～**ENDCG**を記述することもできます。
その場合、上から順に実行されていきます。
複数のSubShaderの時とは違い、順番に全て実行されます。
例えば、モデルに輪郭線を付けたい時は下記のように指定します。

- １パス目で、頂点を法線方向に膨らませて黒ベタで裏面描画
- ２パス目で、通常描画

---

```
#pragma surface surf Lambert
```

**\#pragma surface**でUnityにサーフェイスシェーダである事を宣言しています。
その後の**surf**が、エントリ関数の名前です。
その後の**Lambert**は、ライティングモデルの指定で、ランバート反射を指定しています。
ライティングモデルは、他にもスペキュラや自作のライティング関数を使ったり出来ます。
**\#pragma**は他にも色々オプションが指定できますが、おいおい紹介していきます。

---

```
struct Input {
	float4 color : COLOR;
};
```
次の**surf**関数に渡す構造体の指定です。
**float4**は4つのfloat型がセットになった変数です。
**:**のあとの大文字の**COLOR**は、セマンティクスといって、Unityから渡される情報の意味を指定します。
COLORは、頂点カラー(頂点自体の色)を意味します。
名前の付け方やセマンティクスは決まっていますが、おいおい紹介していきます。

---

```
void surf (Input IN, inout SurfaceOutput o) {
	o.Albedo = half3(0.75, 1.0, 0.5);
}
```

サーフェイスシェーダのエントリ関数です。
引数の**Input IN**は、前述の**Input**構造体の情報が入っています。
**inout SurfaceOutput o**は、入力されたデータを色々して詰め込んで出力する構造体で、下記の変数が入っています。

```
struct SurfaceOutput {
	half3 Albedo;	// 拡散反射光（＝Diffuse）、物体の色
	half3 Normal;	// 法線ベクトル
	half3 Emission;	// 発光色
	half Specular;	// スペキュラ、鏡面反射の強度
	half Gloss;	// 光沢、スペキュラとの違いは今のところ不明…
	half Alpha;	// 不透明度、0で透明、1で不透明
};
```

今回は**o.Albedo**に、half3(0.75, 1.0, 0.5)という色を渡しています。

---

```
FallBack "Diffuse"
```

最後は、全てのSubShaderの実行に失敗した時のフォールバック処理の指定です。
固定関数シェーダで**Diffuse**を指定しています。
省略可能です。

---
## 実行結果

上記のシェーダを実行すると下記のようになります。
![001.PNG](https://qiita-image-store.s3.amazonaws.com/0/22909/50e61de0-66cc-7b93-3c05-061b4ef5eabc.png)

特にライティング等の指定はしてませんが、いい感じに明るい部分と暗い部分ができています。
サーフェイスシェーダ様々です。

---

# プロパティで色を変えてみる

Unityはシェーダの値を外部から変えることができます。
変更後のシェーダは下記になります。

```
Shader "Custom/surfaceshader002" {
	Properties {
		_Color("Diffuse Color",Color) = (1.0, 1.0, 1.0)
	}

	SubShader {
		Tags { "RenderType"="Opaque" }

		CGPROGRAM
		#pragma surface surf Lambert

		struct Input {
			float4 color : COLOR;
		};

		float4 _Color;

		void surf (Input IN, inout SurfaceOutput o) {
			o.Albedo = _Color.rgb;
		}
		ENDCG
	}
	FallBack "Diffuse"
}
```

変更点は、３つあります。

---

```
Properties {
	_Color("Diffuse Color",Color) = (1.0, 1.0, 1.0)
}
```

頭のほうで、Inspector上に表示するプロパティを指定しています。
プロパティは、
**変数名("Inspector上の表示名", 変数の種類) = デフォルト値**
のルールで記述します。
変数の種類は、下記のものが使えます。

```
// 数値系
name ("display name", Range (min, max)) = number // 範囲指定型
name ("display name", Float) = number            // 浮動小数点型
name ("display name", Int) = number              // 整数型

// 色とベクトル
name ("display name", Color) = (number,number,number,number)	// 色
name ("display name", Vector) = (number,number,number,number)	// ベクトル

// テクスチャ系
name ("display name", 2D) = "defaulttexture" {}   // 2Dテクスチャ
name ("display name", Cube) = "defaulttexture" {} // キューブマップ
name ("display name", 3D) = "defaulttexture" {}   // 3Dテクスチャ
```

---

```
float4 _Color;
```

プロパティで受け取ったデータをシェーダ内で使うための定義です。

---

```
o.Albedo = _Color.rgb;
```
**SurfaceOutput o**にプロパティで受け取った**_Color**のrgb値を代入しています。

---
## 実行結果

上記のシェーダを実行すると下記のようになります。

### 実行直後
実行直後の色は白です。
![002.PNG](https://qiita-image-store.s3.amazonaws.com/0/22909/00240601-5f8d-7fdf-e8cb-0170dbb98ed0.png)
![003.PNG](https://qiita-image-store.s3.amazonaws.com/0/22909/fb03727b-f379-973f-d98f-eef9163f80db.png)

### 色を変えてみる
プロパティで色を赤に変えてみました。
![004.PNG](https://qiita-image-store.s3.amazonaws.com/0/22909/4f58a92a-33b7-845e-2734-1d689929b2de.png)
![005.PNG](https://qiita-image-store.s3.amazonaws.com/0/22909/9d4befb6-f92a-37d0-8eac-57dd742c35dc.png)

球体の色も赤くなっています。