Unity でShaderの勉強 その9 ADSシェーディング

##ADSシェーディングとは
Ambient LightingのA
Diffuse LightingのD
Speqular LightingのS
を合わせてADSシェーディングというらしい。
環境光とディフューズ反射、スペキュラ反射を組み合わせてライティングを行うので簡単にリッチな表現ができるらしい。

計算式はこうなる
I(Intensity:強度) = Ia + Id + Is
Ia = La * Ka
Id = Ld * Kd * max((s・n),0)
Is = Ls * Ks * pow((r・n),f)

Lは光の強さを表す
Kは光の反射率を表す
Laの場合、aはambientを表すからLaはambientのLightの強さ=環境光の強さを表すことになる。

Id(ディフューズの強度)は前回?くらいにやったので省略

##Is(スペキュラの強度)について
スペキュラ成分はサーフェイスの輝き具合を表す。
1. 入射光s
2. sの反射光r
3. サーフェイスのノーマルn
4. サーフェイスから視点へのベクトルv
5. vとrの距離に応じてどれくらいスペキュラを小さくするか f

が使われる。

入射光sから反射光rを求めるには以下の式を使う
> r = -s + 2(s・n)n

反射光rによってもたらされる反射の強さは、視点までのベクトルvと全く同じ時に最大となり、rとvがずれると大幅に減衰する。
rとvの内積結果を累乗することでθ分離れた時に急速に値を小さくすることができる。何乗するかはfで表す。
これにスペキュラライトの強度Lsとスペキュラ反射率Ksを組み合わせることでIsの式はいかになる。

> Is = Ls * Ks * Pow((r・v),f)

r・vの結果が0未満になると2乗した時に整数になるからスペキュラがおかしくなるのかな？
ひょっとしたらPow(max(r・v),0),f)の方が良いかもしれない。

##コード

```
Shader "Custom/TestShader" {
	Properties {
		_MainColor("MainColor",Color) = (1,1,1,1)

		_Ka("Ambient反射",Range(0,1)) = 0.5

		_Kd("Diffse反射",Range(0,1)) = 0.5

		_Ks("Speqular反射",Range(0,1)) = 0.5
		_f("Speqular減衰率",Int) = 5

		[Toggle] _useAmbient("Use Ambient",Float) = 1
		[Toggle] _useDiffuse("Use Diffuse",Float) = 1
		[Toggle] _useSpeqular("Use Speqular",Float) = 1

	}

	SubShader {
		Tags { "Queue" = "Geometry" "RenderType" = "Opaque" "LightMode" = "ForwardBase"}
		Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"
			uniform float4 _LightColor0;

			uniform float4 _MainColor;

			uniform float _Ka;

			uniform float _Kd;

			uniform float _Ks;
			uniform int _f;

			uniform float _useAmbient;
			uniform float _useDiffuse;
			uniform float _useSpeqular;

			struct v2f {
				float4 wPos : SV_POSITION;
				float4 ambientColor : Color0;
				float4 diffuseColor : Color1;
				float4 speqularColor : Color2;
			};

			v2f vert(float4 pos : POSITION,float3 normal : NORMAL) {
				v2f output;
				output.wPos = mul(UNITY_MATRIX_MVP,pos);
				float3 wNormal = UnityObjectToWorldNormal(normal);

				//calculate ia
				output.ambientColor = unity_AmbientSky * _Ka;

				//calculate id
				output.diffuseColor = _LightColor0 * _Kd * max(dot(_WorldSpaceLightPos0 ,wNormal),0);

				//calculate is
				float3 r = -_WorldSpaceLightPos0 + 2 * dot(_WorldSpaceLightPos0,wNormal)* wNormal;
				output.speqularColor = _LightColor0 * _Ks * pow(max(dot(r,_WorldSpaceCameraPos),0),_f);
				return output;
			}

			float4 frag(v2f input) : SV_TARGET {
				float4 adsColor = (0,0,0,0);
				if(_useAmbient) {
					adsColor += input.ambientColor;
				}

				if(_useDiffuse) {
					_MainColor += input.diffuseColor;
				}

				if(_useSpeqular) {
					adsColor += input.speqularColor;
				}
				return _MainColor * adsColor;
				
			}
			ENDCG
		}
	}
}
```

結果:正常に描画された。
<img width="483" alt="スクリーンショット 2017-02-23 19.36.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/d0043074-eecd-cf3c-b6c8-fde53493ac44.png">
ライトの向きを変えてみる
<img width="518" alt="スクリーンショット 2017-02-23 19.37.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/ddc0392b-4113-1571-3407-15c66cd211ec.png">
Materialの設定
<img width="342" alt="スクリーンショット 2017-02-23 19.37.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/67575/a21389d5-4964-7b58-e6fc-7bc2d17ed6cd.png">

##注意点
NORMALセマンティクスがNormalとなっていたためUnityObjectToWorldNormal関数が正常に動作せずdiffuseが0になってSphereが真っ暗になっていた。注意。

```lang:正しい
v2f vert(float4 pos : POSITION,float3 normal : NORMAL) {
```

```lang:間違い
v2f vert(float4 pos : POSITION,float3 normal : Normal) {
```