本日はShader勉強枠です。
前回UnityのレファレンスからDiffuse SimpleShaderを見ていきました。
今回はDiffuse SimpleShaderを編集し、学んでいきます。
〇リムライトとは?
リムライトは物体の輪郭に浮かび上がる光ことです。
例えば、下の写真はNASAが撮影した地球です。

この図では地球の大気圏が青く光って見えます。
これをUnity上で再現してみます。まずは何も変哲もない地球のオブジェクトを用意します。

これにリムライトを適応させることで以下のようになります。

参考
〇リムライトに対応するShaderを書く
前回書いたExample/Diffuse Bump Shaderを基にします。
Shader "Example/Diffuse Bump" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
};
sampler2D _MainTex;
sampler2D _BumpMap;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
}
ENDCG
}
Fallback "Diffuse"
}
まずはPropertiesにパラメータを追加します。
Properties {
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
_RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
_RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
}
_RimColorはリムライトの色を指定します。
_RimPowerはリムライトの強さを指定します。
次にテクスチャ情報を記述するInput構造体を追加します。
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
追加したのは
float3 viewDir;
の一行です。 これはカメラからのベクトル、視線方向を処理します。
最後にSurface Shader output構造体を見ていきます。
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow (rim, _RimPower);
}
ここで追加したのは以下の2つです。
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow (rim, _RimPower);
ここで
half rim = 1.0 -saturate(dot(normalize(IN.viewDir),o.Normal))
に関してみていきます。
half型はベクトルやHDRカラーに使用される方です。
saturate(x)はxが0以下なら0に、1以上であれば1にする関数です。
dot(x,y)はx,yの内積を求めます。
normalize(x)はxを正規化します。
ベクトルの正規化はベクトルの方向は維持しつつ大きさを1にすることです。(単位ベクトルにします。)
よって
rim = 1.0 -正規化したINのviewDirとo.Normalの内積(0~1の値を取る)
となります。
上記式からrimは0~1になることがわかります。
o.Emission
は発光に関する定義です。
Pow(rim,_RimPower);
はrimの_Rimpower乗を意味します。(Pow(x,y)でxのy乗を意味します。)
よって
o.Emission = _RimColor.rgb*pow(rim,_RimPoewer);
は
発光=_RimColorのRGB×Rim^_RimPower
を意味します。rimは0~1を取ります。 よって発光も0~1の値を取ることがわかります。
この値を発行として適応しています。
これを記述したものが以下になります。
//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse RimLight"{
Properties{
_MainTex("Texture",2D) = "white"{}
_BumpMap("Bumpmap", 2D) = "bump"{}
_RimColor("Rim Color", Color) = (0.26,0.19,0.0)
//リムpowerのレンジ defaultでは3に設定される。
_RimPower("Rim Power", Range(0.5,8.0)) = 3.0
}
//Shaderの中身サブシェーダーではレンダリングパスの一覧を定義し、任意のオプションとしてすべてのパスに共通の State (状態)を設定します。追加で、サブシェーダーの特定の Tag を設定できます。
SubShader{
//RenderingType
Tags{"RenderType" = "Opaque"}
//
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimPower;
void surf(Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
//rim = 1.0 -viewDirの正規化したものとノーマルマップの内積(0~1の値を取る)
half rim = 1.0 - saturate(dot(normalize(IN.viewDir),o.Normal));
//Emissionに代入 propertyのRimColorの値(RGB)× rim^_RimPower= 0~1の値を取る 参考https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-pow
o.Emission = _RimColor.rgb * pow(rim, _RimPower);
}
ENDCG
}
Fallback "Diffuse"
}
またPropertiesのRimPowerはRangeとして記述しているためUnityのMaterial側で0.5から8に値の間でスライダーで値が調整できます。
ここでもう一度Shaderの中身に戻ると
o.Emission = _RimColor.rgb * pow(rim, _RimPower);
の
pow(rim,_RimPower);
でわかるようにRimPowerの値を上げていくとo.Emissionの値が小さくなることがわかります。rimは0~1の値で例えばrim=1の場合1のべき乗は1になることから1になりますが、rim=0.5でRimPowerの値が高くなれば高くなるほどo.Emissionの値が小さくなることがわかります。
Unityのマテリアルからスライダーの値を上げるとリムライトの強さが小さくなることがわかります。
