夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

UnityでShaderを勉強する その⑥ リムライトを扱うShaderを書く

本日はShader勉強枠です。

前回UnityのレファレンスからDiffuse SimpleShaderを見ていきました。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

今回はDiffuse SimpleShaderを編集し、学んでいきます。

〇リムライトとは?

 リムライトは物体の輪郭に浮かび上がる光ことです。

 例えば、下の写真はNASAが撮影した地球です。

 f:id:Holomoto-Sumire:20200419161233p:plain

 この図では地球の大気圏が青く光って見えます。

 これをUnity上で再現してみます。まずは何も変哲もない地球のオブジェクトを用意します。

 

f:id:Holomoto-Sumire:20200419161511j:plain
リムライトを適応していない地球のオブジェクト

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

 

f:id:Holomoto-Sumire:20200419161746j:plain
リムライトを適応した地球のオブジェクト

参考

drawtip.net

〇リムライトに対応する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にすることです。(単位ベクトルにします。)

yaju3d.hatenablog.jp

よって

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のマテリアルからスライダーの値を上げるとリムライトの強さが小さくなることがわかります。

f:id:Holomoto-Sumire:20200420083645g:plain