夜風のMixedReality

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

Unityのシェーダーで影の部分のみにテクスチャが描画されるような実装をおこなう

本日はShader枠です。

本日はライトの影響を受けるLitなシェーダーで影の部分のみにテクスチャが描画されるような実装を行います。

〇ベースシェーダー

今回はこちらのシンプルなライティングを受けるシェーダーをベースにしていきます。

Shader "Unlit/TutorialShader"
{
    Properties
    {
     //   _MainTex ("Texture", 2D) = "white" {}
        _MainColor("Color" ,color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include  "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            
            struct appdata
            {
                float4 vertex : POSITION;
                half3 normal: NORMAL;
            };

            struct v2f
            {
            float4 vertex : SV_POSITION;
            float3 normalWS : TEXCOORD1;
            };


            float4 _MainColor;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = TransformObjectToHClip(v.vertex);
                //面の法線を取得、ライトの当たる向きを計算
                VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
                o.normalWS = normal.normalWS;
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                float4 col = _MainColor;
                //Light.hlslで提供されるUnityのライトを取得する関数
                Light lt = GetMainLight();

                //ライトの向きを計算
                float strength = saturate(dot(lt.direction, i.normalWS));
                float4 lightColor = float4(lt.color, 1);
                return col* lightColor*strength;
            }
            ENDHLSL
        }
    }
}

このシェーダーはこちらの記事で解説しています。

redhologerbera.hatenablog.com

このシェーダーではURP専用にDirectonalLightの影響を受け影が付くようなシェーダーとなっています。

〇影についてのおさらい

UnityのURPの場合Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlslでライトに関してのマクロが提供されています。

ここではLight lt = GetMainLight();でライト情報を格納しています。

最終的にcol lightColorstrengthで元の色、ライトの色、影を合成しています。

影とはライトの当たっていない部分になりますが、実際のところstrengthでは影の範囲(黒い範囲)を意味しています。

つまり saturate(1- strength)によって影の範囲を正の値として取得できることになります。

次のようにshadowという変数を置くことでshadowという変数を用いて影の領域のみの処理が可能になります。

            float4 frag (v2f i) : SV_Target
            {
                float4 col = _MainColor;
                //Light.hlslで提供されるUnityのライトを取得する関数
                Light lt = GetMainLight();

                //ライトの向きを計算
                float strength = saturate(dot(lt.direction, i.normalWS));
                float4 lightColor = float4(lt.color, 1);
                float shadow = saturate(1-strength);//変更
                return col* lightColor*shadow;
            }

〇テクスチャの定義

次にマテリアルからテクスチャを設定できるようにします。

具体的には次のコードです 変更点は①、②です。

    Properties
    {
     //   _MainTex ("Texture", 2D) = "white" {}
        _MainColor("Color" ,color) = (1,1,1,1)
        _ShadowTex("ShadowTe",2D) = "white"{}//Unityのマテリアル側でのパブリックな変数  ①
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include  "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            


            float4 _MainColor;
            sampler2D _ShadowTex;//Samplerを定義  ②
    ・・・

次にテクスチャを使用するためにuvを定義します。

            struct appdata
            {
                float4 vertex : POSITION;
                half3 normal: NORMAL; 
                float2 uv : TEXCOORD0; //追加
            };

            struct v2f
            {
            float4 vertex : SV_POSITION;
            float2 uv : TEXCOORD; //追加
            float3 normalWS : TEXCOORD1;
            };


            float4 _MainColor;
            sampler2D _ShadowTex;
            v2f vert (appdata v)
            {
                v2f o;
                o.uv = v.uv //追加
    ・・・
                return o;
            }

最後にtex2D()を使用してShadow部のみにテクスチャが反映されるようにします。

            float4 frag (v2f i) : SV_Target
            {
                float4 col = _MainColor;
                //Light.hlslで提供されるUnityのライトを取得する関数
                Light lt = GetMainLight();

                //ライトの向きを計算
                float strength = saturate(dot(lt.direction, i.normalWS));
                float4 lightColor = float4(lt.color, 1);
                float shadow =(1-smoothstep(0,0.5,strength)) * tex2D(_ShadowTex,i.uv) ;
                return col*lightColor*strength+shadow;
           
            }

shadowを最後に足しているのは合成の演算で足し算(Add)の場合影の部分のみにテクスチャの成分が合成される為です。

これによって影の部分にテクスチャが描画されるような実装ができました。

このシェーダーの用途としては例えば地球を再現する際など影となっている部分は真っ暗ではなく夜を再現するといった場合に使えます。

本日は以上です。

〇シェーダー全文

Shader "Unlit/TutorialShader"
{
    Properties
    {
     //   _MainTex ("Texture", 2D) = "white" {}
        _MainColor("Color" ,color) = (1,1,1,1)
        _ShadowTex("ShadowTe",2D) = "white"{}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include  "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            
            struct appdata
            {
                float4 vertex : POSITION;
                half3 normal: NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
            float4 vertex : SV_POSITION;
            float2 uv : TEXCOORD;
            float3 normalWS : TEXCOORD1;
            };


            float4 _MainColor;
            sampler2D _ShadowTex;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = TransformObjectToHClip(v.vertex);
                //面の法線を取得、ライトの当たる向きを計算
                VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
                o.uv = v.uv;
                o.normalWS = normal.normalWS;
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                float4 col = _MainColor;
                //Light.hlslで提供されるUnityのライトを取得する関数
                Light lt = GetMainLight();

                //ライトの向きを計算
                float strength = saturate(dot(lt.direction, i.normalWS));
                float4 lightColor = float4(lt.color, 1);
                float shadow =(1-smoothstep(0,0.5,strength)) * tex2D(_ShadowTex,i.uv) ;
                return col*lightColor*strength+shadow;
            }
            ENDHLSL
        }
    }
}