前回は氷のような中央部の透明度が高く、周辺の透明度が高いシェーダを作成しました。
今回は前回作った氷のシェーダを利用して、背後から光が当たったようなリムライティングと呼ばれるシェーダを作ってみましょう。
リムライティングとは
リムライティングは、3Dモデルの後ろからライトが当たっている演出のことで、3Dモデルの後ろ側から当たったライトの光が、モデルの手前側に回り込むことで輪郭部分が強調されます。
リムライティングは、言葉で説明するよりも絵で見たほうが分かりやすいので、色々と良さそうな画像を探していたところ・・・・とても秀逸な説明画像を見つけました!
このドラマ、めちゃくちゃ好きでした。
さて、なんのドラマでしょうか?
リムライティングのシェーダプログラム
リムライティングシェーダのプログラムは次のようになります。前回作った氷のプログラムとほぼ同じなので理解しやすいと思います。
Shader "Custom/sample" { SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard #pragma target 3.0 struct Input { float2 uv_MainTex; float3 worldNormal; float3 viewDir; }; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 baseColor = fixed4(0.05, 0.1, 0, 1); fixed4 rimColor = fixed4(0.5,0.7,0.5,1); o.Albedo = baseColor; float rim = 1 - saturate(dot(IN.viewDir, o.Normal)); o.Emission = rimColor * pow(rim, 2.5); } ENDCG } FallBack "Diffuse" }
リムライティングのプログラムでは、オブジェクトの輪郭部分のエミッションを高くすることで、背後から光が当たっているような演出になります。周辺部分の色を変えるという意味では、前回のアルゴリズムと同じものを使っています。したがって、サーフェイスシェーダにはworldNormal(オブジェクトの法線ベクトル)とviewDir(視線ベクトル)を入力しています。
輪郭部分を強調するアルゴリズムを簡単におさらいすると・・・輪郭部分では視線ベクトルと法線ベクトルが垂直に近い角度で交わるのに対して、中央部分ではほぼ平行に近い角度で交わります。リムライティングでは視線ベクトルと法線ベクトルが垂直に近い場合だけ、エミッションの値を高くすれば良さそうです。
視線ベクトルと法線ベクトルの交わる角度を求めるため、worldNormalとviewDirの内積をとっています。
worldNormal * viewDir = |worldNormal| * |viewDir| * cosθ = cosθ
視線ベクトルと法線ベクトルの内積は、垂直に交わる場合は0、平行に交わる場合には1または-1になります。2つのベクトルが垂直に交わる場合にはエミッションを1、並行の場合はエミッションを0にするために絶対値をとって1から引いています。これにより最終的なエミッションの計算結果は、垂直に交わる場合には1、平行に交わる場合には0になります。
rim = 1 - abs( cosθ );
この状態でrimの値は 0 < rim < 1なので、このrim係数をrimColorにかけてからSurfaceOutputStandardのEmissionに代入すればよさそうです。
o.Emission = rimColor * rim;
この計算により、周辺部分のエミッションは高いままで、中央部分に行くに従ってエミッションが低くなるはずです。実行結果は次のとおりです。
光の減衰を調節する
一応、輪郭部分のエミッションは高くなっていますが、中央部分まで光が回り込んでしまって、シャープな輪郭は出ていません(これはこれで綺麗ですが・・・)この原因は、rim変数の減衰がcos関数に従うため、中央付近まで光が減衰しないからです。
シャープに光を減衰させるために、次のようにrimを3乗したものをrimColorに乗算してからEmissionに代入します。
o.Emission = rimColor * pow(rim, 3);;
rimを3乗したことで減衰速度が急峻になり、これにより綺麗に輪郭のみが浮かび上がります。
リムライティング、古畑任三郎でした・・・・
私の書いた「Unityの教科書」もよろしくお願いします!