【Unity】理想的な陰影をつけるトゥーンシェーダ



# Standard Assetsのトゥーンシェーダ
3Dモデルをアニメ調で表現してくれるトゥーンシェーダ。いろんなところで使えます。
試しにユニティちゃんのシェーダをUnityのStandard AssetについてくるToon Litで目の部分以外を置き換えると
![pic1.png](https://qiita-image-store.s3.amazonaws.com/0/113595/ab7c7898-9043-2bdd-014b-4763c955a433.png)
このように良い感じに表示されますが
ユニティちゃんを180度回転すると
![pic2.png](https://qiita-image-store.s3.amazonaws.com/0/113595/2a63f926-dd43-48e9-2df3-17959e145ebc.png)
影が強すぎて全く良い感じじゃありません。
しかもToonなのに徐々に影がついています。

これを解消する手段としては
1. Directional LightのStrengthを弱める
2. Mesh RendererのReceive ShadowsをOffにする
3. Toon Litを諦めてToon Basicにする
等が挙げられますが1はシーン全体のCast Shadowが弱くなってしまう、2は物陰に移動したとき影が映らない、3は負け
とメリットデメリットがあります。

# なんでそんな陰になっちゃうの
少しシェーダの話をします。
Toon Litシェーダのライティング処理部分を見てみると

```
inline half4 LightingToonRamp (SurfaceOutput s, half3 lightDir, half atten)
{
	#ifndef USING_DIRECTIONAL_LIGHT
	lightDir = normalize(lightDir);
	#endif
	
	half d = dot (s.Normal, lightDir)*0.5 + 0.5;
	half3 ramp = tex2D (_Ramp, float2(d,d)).rgb;
	
	half4 c;
	c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
	c.a = 0;
	return c;
}
```

この部分でToonの陰影処理を行っているのですが
変に強い影ができてしまう原因は**attenの位置**にあります。
attenはライトの減衰率(attenuation)のことです。
物陰など暗い部分だとattenは0に近づき、明るい場所だと1に近づきます。

通常なら、ToonRampMapで指定した色を外れることなく陰影をつけたいのですが
Toon Rampの色を取得した後、つまり
`half3 ramp = tex2D (_Ramp, float2(d,d)).rgb;`
で色を取得した後に
`c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);`
でattenをかけて最終的な色を決めてしまっているので、表示される色が黒っぽくなってしまっています。

では、どうすればよいのかというと
attenをかける処理の記述場所を変えます。
つまり、Toon Rampの色を取得する段階でattenを適用させてしまいます。

書き換えたのがこちらです。

```
inline half4 LightingToonRamp (SurfaceOutput s, half3 lightDir, half atten)
{
	#ifndef USING_DIRECTIONAL_LIGHT
	lightDir = normalize(lightDir);
	#endif
	
	half d = (dot (s.Normal, lightDir) * 0.5 + 0.5) * atten; // ここで減衰率を適用させる
	half3 ramp = tex2D (_Ramp, float2(d,d)).rgb; // Toon Rampの色を取得するためのパラメータとしてattenを利用する
	
	half4 c;
	c.rgb = s.Albedo * _LightColor0.rgb * ramp * 2; // attenを削除
	c.a = 0;
	return c;
}
```

こうすることで、最終的な影の強さはToon Rampの範囲を越えなくなります。
書き換えたシェーダでユニティちゃんを表示させると
![pic3.png](https://qiita-image-store.s3.amazonaws.com/0/113595/b3c20e77-549e-67b5-f1c0-93cd7302a6c2.png)
Directional Lightに対して背を向けていても
![pic4.png](https://qiita-image-store.s3.amazonaws.com/0/113595/d0ff993d-fad8-c65a-ab33-bb585742f9f5.png)
![pic5.png](https://qiita-image-store.s3.amazonaws.com/0/113595/622f73d1-5b57-6a31-342c-ac98a8d98345.png)
物陰に隠れていてもToon Rampの影の色を越えることはなくなります。

これで、Receive Shadowsもちゃんと行い、影もToon Rampの範囲を越えない理想的なトゥーンシェーダとなりました。

# シェーダコード全文
コード全文です。
新しくShaderを作成し、以下をコピペすると動くはずです。
名前はCustom/Toon Lit Receive Shadowsとしときます。

```
Shader "Custom/Toon Lit Receive Shadows" {
	Properties{
		_Color("Main Color", Color) = (.5,.5,.5,1)
		_MainTex("Base (RGB)", 2D) = "white" {}
		_Ramp("Toon Ramp (RGB)", 2D) = "gray" {}
	}

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

		CGPROGRAM
		#pragma surface surf ToonRamp

		sampler2D _Ramp;

		// custom lighting function that uses a texture ramp based
		// on angle between light direction and normal
		#pragma lighting ToonRamp exclude_path:prepass
		inline half4 LightingToonRamp (SurfaceOutput s, half3 lightDir, half atten)
		{
    		#ifndef USING_DIRECTIONAL_LIGHT
    			lightDir = normalize(lightDir);
    		#endif

    			half d = (dot (s.Normal, lightDir) * 0.5 + 0.5) * atten; // ここで減衰率を適用させる
    			half3 ramp = tex2D (_Ramp, float2(d,d)).rgb; // Toon Rampの色を取得するためのパラメータとしてattenを利用する

    			half4 c;
    			c.rgb = s.Albedo * _LightColor0.rgb * ramp * 2; // attenを削除
    			c.a = 0;
    			return c;
		}

		sampler2D _MainTex;
		float4 _Color;

		struct Input {
			float2 uv_MainTex : TEXCOORD0;
		};

		void surf(Input IN, inout SurfaceOutput o) {
			half4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Alpha = c.a;
		}
		ENDCG

	}

	Fallback "Diffuse"
}
```

# まとめ
Standard Assetsのトゥーンシェーダの少し残念なところを改良し、理想的な陰影をするトゥーンシェーダを作成しました。
ちなみに、私のUnity環境下(5.1.1f1)におけるToon Litの話ですので
最新バージョンはどうなってるかわかりません。

皆様もこれを機にシェーダコーディングをしてみてはいかがでしょうか。

![imageLicenseLogo.png](https://qiita-image-store.s3.amazonaws.com/0/113595/ae833d08-68e7-fbf5-f0c8-c9f215a085ec.png)

このコンテンツは、『[ユニティちゃんライセンス](http://unity-chan.com/contents/license_jp/)』で提供されています