Shader shadow casting in Unity

Article directory

  • Preface
  • 1. We urgently need to use the Shader in the previous Offset as a test
  • 2. Generate shadows
    • 1. Add the Pass of “LightMode” = “ShadowCaster”.
    • 2. Declare float4 vertex:POSITION; and half3 normal:NORMAL; in appdata. This is the semantics required to generate shadows.
    • 3. Add V2F_SHADOW_CASTER in v2f; used to declare the data that needs to be transferred to the fragment.
    • 4. Add TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) in the vertex shader, mainly to calculate the offset of the shadow to solve the incorrect Shadow Acne and Peter Panning phenomena.
    • 5. Add SHADOW_CASTER_FRAGMENT(i) in the fragment shader
  • 3. Let the shadow and the little fox change together as they dissolve
    • 1. The logic of previous little fox ablation can be reused

Foreword

Shader shadow casting in Unity

1. We urgently need to use the Shader in the previous Offset as a test

Shader’s depth offset Offset in Unity

We will find that the little fox has no shadow at this time. This is because our Shader does not write shadow casting.

2. Generate shadow

1. Add “LightMode” = “ShadowCaster” Pass.

Tags{“LightMode” = “ShadowCaster”}

Need to add a Unity variant

#pragma multi_compile_shadowcaster
Defined in the Pass of LightMode=ShadowCaster, two macros will be automatically generated:

  1. SHADOWS_DEPTH: Used to generate linear light and spotlight shadows.
  2. SHADOW_CUBE: used to generate point light shadows.

2. Declare float4 vertex:POSITION; and half3 normal:NORMAL; in appdata. This is the semantics required to generate shadows.

Note: In the appdata section, we almost do not need to modify the name and the corresponding type.
Because many methods encapsulated in Unity use these standard names and types

struct appdata
{
float4 vertex:POSITION;
half3 normal:NORMAL;
};

3. Add V2F_SHADOW_CASTER in v2f; used to declare the data that needs to be transferred to the fragment.

struct v2f
{
V2F_SHADOW_CASTER;
};

4. Add TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) in the vertex shader, mainly to calculate the offset of the shadow to solve the incorrect Shadow Acne and Peter Panning phenomena.

v2f vert(appdata v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
}

5. Add SHADOW_CASTER_FRAGMENT(i) in the fragment shader

fixed4 frag(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i);
}

As you can see, our little fox now has a shadow
Please add an image description
After writing this, we can control whether to cast shadows in the Mesh Renderer
Please add a picture description

3. Let the shadow and the little fox change together as they dissolve

1. The logic of previous little fox ablation can be reused

Final code:

Shader "MyShader/P1_7_2"
{
    Properties
    {
        [Enum(Off,0,On,1)]_ZWrite("ZWrite",int) = 0
        [Enum(UnityEngine.Rendering.CompareFunction)]_ZTest("ZTest",int) = 0
        //Use this tag to expose external attributes and have titles.
        [Header(Base)]
        [NoScaleOffset]_MainTex ("Texture", 2D) = "white" {}
        _Clip("Clip",Range(0,1)) = 0
        //Use this tag to add a gap between two rows of exposed attributes
        [Space(10)]
        [Header(Dissolve)]
        _DissolveTex("DissolveTex",2D) = "black"{}

        [NoScaleOffset]_RampTex("RampTex(RGB)",2D) = "black" {}
        
    }
    SubShader
    {
        Tags{"Queue" = "Geometry"}
        Blend Off
        Cull Back
        /*ZWrite [_ZWrite]
        
        ZTest [_ZTest]*/
        
        Offset -1,-1
        
        UsePass "MyShader/P1_6_4/XRay"
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment fragment
            #include "UnityCG.cginc"
            
            sampler2D _MainTex;
            float _Clip;
            sampler2D _DissolveTex;
            //This four-dimensional vector, xyzw represents the xy of Tilling and Offset respectively. The naming method is to add _ST after the texture name.
            float4 _DissolveTex_ST;


            //Because when using the gradient texture, only the u coordinate of the gradient texture is used, so replace sampler2D with sampler
            sampler _RampTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float4 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                
                //In order to reduce the value passed in, we do not create new variables for storage, but change uv to a four-dimensional vector.
                //Use xy of o.uv to store the original character map
                //Use zw of o.uv to store the scaled and offset values of the noise map
                o.uv.xy = v.uv.xy;
                //o.uv.zw = v.uv * _DissolveTex_ST.xy + _DissolveTex_ST.zw;

                o.uv.zw = TRANSFORM_TEX(v.uv,_DissolveTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv.xy);
                //Texture obtained externally needs to be sampled before use
                fixed4 dissolveTex = tex2D(_DissolveTex,i.uv.zw);
                
                //fragment selection
                clip(dissolveTex.r - _Clip);

                //Perform normalization
                fixed4 dissolveValue = saturate((dissolveTex.r - _Clip) / (_Clip + 0.1 - _Clip));

                fixed4 rampTex = tex1D(_RampTex,dissolveValue.r);

                //col + = rampTex;
                return col;
            }
            ENDCG
        }
        //Shadow casting
        Pass
        {
            //1. Set "LightMode" = "ShadowCaster"
            Tags{"LightMode" = "ShadowCaster"}
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment fragment
            //Need to add a Unity variant
            #pragma multi_compile_shadowcaster

            
            #include "UnityCG.cginc"

            //Declare variables used for ablation
            float _Clip;
            sampler2D _DissolveTex;
            float4 _DissolveTex_ST;
            
            //2. Declare float4 vertex:POSITION; and half3 normal:NORMAL; in appdata. This is the semantics required to generate shadows.
            //Note: In the appdata part, we almost don't need to modify the name and corresponding type.
            //Because many methods encapsulated in Unity use these standard names.
            struct appdata
            {
                float4 vertex:POSITION;
                half3 normal:NORMAL;
                float4 uv:TEXCOORD;
            };
            //3. Add V2F_SHADOW_CASTER in v2f; used to declare the data that needs to be transferred to the fragment.
            struct v2f
            {
                float4 uv : TEXCOORD;
                V2F_SHADOW_CASTER;
            };
            //4. Add TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) in the vertex shader, mainly to calculate the offset of the shadow to solve the incorrect Shadow Acne and Peter Panning phenomena.
            v2f vert(appdata v)
            {
                v2f o;
                o.uv.zw = TRANSFORM_TEX(v.uv,_DissolveTex);
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
                return o;
            }
            //5. Add SHADOW_CASTER_FRAGMENT(i) in the fragment shader
            
            fixed4 frag(v2f i) : SV_Target
            {
                //Externally obtained texture needs to be sampled before use
                fixed4 dissolveTex = tex2D(_DissolveTex,i.uv.zw);
                
                //fragment selection
                clip(dissolveTex.r - _Clip);
                
                SHADOW_CASTER_FRAGMENT(i);
            }
            ENDCG
        }
    }
}

Finally we saw that the shadow melted with the little fox and also disappeared at the same time.
Please add a picture description