Standard Assetsのトゥーンシェーダ
3Dモデルをアニメ調で表現してくれるトゥーンシェーダ。いろんなところで使えます。
試しにユニティちゃんのシェーダをUnityのStandard AssetについてくるToon Litで目の部分以外を置き換えると
このように良い感じに表示されますが
ユニティちゃんを180度回転すると
影が強すぎて全く良い感じじゃありません。
しかもToonなのに徐々に影がついています。
これを解消する手段としては
- Directional LightのStrengthを弱める
- Mesh RendererのReceive ShadowsをOffにする
- 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の範囲を越えなくなります。
書き換えたシェーダでユニティちゃんを表示させると
Directional Lightに対して背を向けていても
物陰に隠れていても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の話ですので
最新バージョンはどうなってるかわかりません。
皆様もこれを機にシェーダコーディングをしてみてはいかがでしょうか。
このコンテンツは、『ユニティちゃんライセンス』で提供されています