39
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

Standard Assetsのトゥーンシェーダ

3Dモデルをアニメ調で表現してくれるトゥーンシェーダ。いろんなところで使えます。
試しにユニティちゃんのシェーダをUnityのStandard AssetについてくるToon Litで目の部分以外を置き換えると
pic1.png
このように良い感じに表示されますが
ユニティちゃんを180度回転すると
pic2.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
Directional Lightに対して背を向けていても
pic4.png
pic5.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

このコンテンツは、『ユニティちゃんライセンス』で提供されています

39
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?