おもちゃラボ

Unityで遊びを作ってます

【Unityシェーダ入門】リムライティングのシェーダを作る

前回は氷のような中央部の透明度が高く、周辺の透明度が高いシェーダを作成しました。

nn-hokuson.hatenablog.com

今回は前回作った氷のシェーダを利用して、背後から光が当たったようなリムライティングと呼ばれるシェーダを作ってみましょう。

リムライティングとは

リムライティングは、3Dモデルの後ろからライトが当たっている演出のことで、3Dモデルの後ろ側から当たったライトの光が、モデルの手前側に回り込むことで輪郭部分が強調されます。

f:id:nn_hokuson:20161008221541p:plain

リムライティングは、言葉で説明するよりも絵で見たほうが分かりやすいので、色々と良さそうな画像を探していたところ・・・・とても秀逸な説明画像を見つけました!

このドラマ、めちゃくちゃ好きでした。

さて、なんのドラマでしょうか?

☆PROJECT ASURA☆ [OpenGL] 『リムライティング』

リムライティングのシェーダプログラム

リムライティングシェーダのプログラムは次のようになります。前回作った氷のプログラムとほぼ同じなので理解しやすいと思います。

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(視線ベクトル)を入力しています。

輪郭部分を強調するアルゴリズムを簡単におさらいすると・・・輪郭部分では視線ベクトルと法線ベクトルが垂直に近い角度で交わるのに対して、中央部分ではほぼ平行に近い角度で交わります。リムライティングでは視線ベクトルと法線ベクトルが垂直に近い場合だけ、エミッションの値を高くすれば良さそうです。

f:id:nn_hokuson:20161007214918p:plain

視線ベクトルと法線ベクトルの交わる角度を求めるため、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;

この計算により、周辺部分のエミッションは高いままで、中央部分に行くに従ってエミッションが低くなるはずです。実行結果は次のとおりです。

f:id:nn_hokuson:20161008231632p:plain

光の減衰を調節する

一応、輪郭部分のエミッションは高くなっていますが、中央部分まで光が回り込んでしまって、シャープな輪郭は出ていません(これはこれで綺麗ですが・・・)この原因は、rim変数の減衰がcos関数に従うため、中央付近まで光が減衰しないからです。

f:id:nn_hokuson:20161008233057p:plain

シャープに光を減衰させるために、次のようにrimを3乗したものをrimColorに乗算してからEmissionに代入します。

o.Emission = rimColor *  pow(rim, 3);;

rimを3乗したことで減衰速度が急峻になり、これにより綺麗に輪郭のみが浮かび上がります。

f:id:nn_hokuson:20161008233218p:plain

リムライティング、古畑任三郎でした・・・・
私の書いた「Unityの教科書」もよろしくお願いします!