Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影

文章目录

  • Shadow Caster
    • Using URP Shadow Caster Pass
    • Using Custom Shadow Caster Pass
      • 先来看看 [没有] apply shadow bias 版本的
      • 再来看看 [有] apply shadow bias 版本的
    • CBuffer 定义位置建议
      • 会不会浪费 CBUFFER 空间
  • Shadow Rerieve
  • 优化
    • Shadow Pancaking
    • Shadow Distance Fade out
    • 提升阴影的掠射角的质量
  • 完整的 Shader
  • References
  • Extended reading


因为近期需要将之前 Built-in RP 升级到 URP,所以啊,要调整的内容真的是多

其中,在以前自定义 shader 中使用到 阴影的部分,都需要调整一下,使用 URP 自带的 shader API(如果自己写阴影方案,但是又没有 unity 引擎源码,你会遇到一堆问题,例如,你可以看看我之前写的一篇:Unity Shader - Custom Shadow Map (New Version) - 这其中就遇到 LOD 问题ReplaceShader 功能不够强大的问题


Shadow Caster

  • 使用 URP 中现有 Shader 的 Shadow Caster Pass
  • 自己写一个,一般如果做项目的话,都自己写,出问题也能自己修改

Using URP Shadow Caster Pass

OK,下面将的是:使用 URP 的 Shadow Caster Pass
ShaderLab 中引用其他 Shader 中的 Pass 是很简单的用到:UsePass,如下:

        // jave.lin : 使用 Universal 中自带的 Universal Render Pipeline/Lit Shader 中的 ShadowCaster Pass
        UsePass "Universal Render Pipeline/Lit/ShadowCaster"

用现成的多数情况下是比较方便的,但就是限制在使用现有的功能,虽然说可以修改 URP 的 shader ,但是如果你升级 URP 版本就不方便了

使用 URP 的 Shadow Caster 需要注意:该 Pass 使用了 Instancing,如果你的材质没有开启Instancing 开关,那么可以在 FrameDebugger 看到是没有合批的,一个一个的绘制,效率低下,如下图:
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第1张图片
如果我们在材质勾上 instancing 就可以看到合批了
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第2张图片

有两个选择:那么要 instancing? 还是 SRP Batcher 呢?这要看你自己去权衡,但是这里有一些建议:

  • 如果大量绘制的网格一致,那么建议用 instancing
    • 如果用上 instancing,那么你的其他 pass 最好都同步添加上 instancing 的支持
  • 如果网格不一致,但是 shader ,且 shader 变体一致,那么建议用 SRP Batcher

如何 SRP Batcher 主要是将 uniform 变量都划分到对应的 CBuffer 块中,可以查看我之前一篇:Unity Shader - shader lab 的 SRP Batcher compatible 兼容性


Using Custom Shadow Caster Pass

OK,下面将的是自定义的 Shadow Caster Pass

自定义的好处就是,可以在很多功能上根据自己的需求来设置,最大化优化,但前提就是你要足够熟悉

从文档、还有 URP 的代码来看,shader 变体 需要注意几点:

  • Shader Pass 的 LightMode Tag,要设置为:ShadowCaster,Like This : Tags{"LightMode" = "ShadowCaster"} 底层 pass filter 需要

  • #pragma multi_compile _ _MAIN_LIGHT_SHADOWS 控制是否开启、关闭接受功能的 变体,在 Light 的 Component 中调整:Shadow Type 即可看到效果

    • Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第3张图片
  • #pragma shader_feature _ALPHATEST_ON shader_feature 的 alpha test 变体,便于树叶之类的镂空需要

    • 同时 shader properties 添加上:[Toggle] _ALPHATEST ("Alpha Test On", Float) = 0 便于材质上调整
    • Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第4张图片

总之 shader 变体就设置下面几个就好:

            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma shader_feature _ALPHATEST_ON

如果说,你能确定你的 shader 不需要某些变体,如:已确定:需要阴影、且是软阴影,且是 alpha test 镂空,那么直接 #define 即可,不需要 Off 的情况,那么将这些变体宏统统改为 #define 明文定义,如下:

            #define _MAIN_LIGHT_SHADOWS
            #define _SHADOWS_SOFT
            #define _ALPHATEST_ON

这样即可减少变体数量,但是代价就是不能中途同过 Shader.EnabledKeyword, 或是 Shader.DisabledKeyword C# 脚本API 来控制变体开关了,这需要你根据自己项目实际情况来选择


先来看看 [没有] apply shadow bias 版本的

这和 Built-in RP 中的应用算法不太一样,但是这个可读性比 Built-in RP 的高很多,这两个参数具体在:
UniversalRenderPipelineAsset 资源的 Shadow/Depth Bias 和 Normal Bias
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第5张图片

  • Depth Bias 是,相对灯光方向的深度偏移
  • Normal Bias 是相对 Rim 边缘强度做深度偏移

这两个参数都可以在:Shadows.hlsl 文件中的 ApplyShadowBias 方法中看到是如何应用的:

float3 ApplyShadowBias(float3 positionWS, float3 normalWS, float3 lightDirection)
{
    float invNdotL = 1.0 - saturate(dot(lightDirection, normalWS));
    float scale = invNdotL * _ShadowBias.y;

    // normal bias is negative since we want to apply an inset normal offset
    positionWS = lightDirection * _ShadowBias.xxx + positionWS;
    positionWS = normalWS * scale.xxx + positionWS;
    return positionWS;
}

对应 Shader 的 Shadow Caster Pass 如下

        Pass // jave.lin : 没有 ApplyShadowBias
        {
            Name "ShadowCaster"
            Tags{ "LightMode" = "ShadowCaster" }
            HLSLPROGRAM
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #pragma vertex vert
            #pragma fragment frag
            #pragma shader_feature _ALPHATEST_ON
            // jave.lin : 根据你的 alpha test 是否开启而定
            //#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            struct a2v {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct v2f {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            v2f vert(a2v v)
            {
                v2f o = (v2f)0;
                o.vertex = TransformObjectToHClip(v.vertex.xyz);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            real4 frag(v2f i) : SV_Target
            {
#if _ALPHATEST_ON
                half4 col = tex2D(_MainTex, i.uv);
                clip(col.a - 0.001);
#endif
                return 0;
            }
            ENDHLSL
        }

再来看看 [有] apply shadow bias 版本的

应用了 两个 bias 参数后,在 Depth Bias, Normal Bias 调整才能看到效果
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第6张图片

shader 的 Shadow Caster Pass,如下:

        Pass // jave.lin : 有 ApplyShadowBias
        {
            Name "ShadowCaster"
            Tags{ "LightMode" = "ShadowCaster" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            struct a2v {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };
            struct v2f {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            // 以下三个 uniform 在 URP shadows.hlsl 相关代码中可以看到没有放到 CBuffer 块中,所以我们只要在 定义为不同的 uniform 即可
            float3 _LightDirection;
            float4 _ShadowBias; // x: depth bias, y: normal bias
            half4 _MainLightShadowParams;  // (x: shadowStrength, y: 1.0 if soft shadows, 0.0 otherwise)
            // jave.lin 直接将:Shadows.hlsl 中的 ApplyShadowBias copy 过来
            float3 ApplyShadowBias(float3 positionWS, float3 normalWS, float3 lightDirection)
            {
                float invNdotL = 1.0 - saturate(dot(lightDirection, normalWS));
                float scale = invNdotL * _ShadowBias.y;
                // normal bias is negative since we want to apply an inset normal offset
                positionWS = lightDirection * _ShadowBias.xxx + positionWS;
                positionWS = normalWS * scale.xxx + positionWS;
                return positionWS;
            }
            v2f vert(a2v v)
            {
                v2f o = (v2f)0;
                float3 worldPos = TransformObjectToWorld(v.vertex.xyz);
                half3 normalWS = TransformObjectToWorldNormal(v.normal);
                worldPos = ApplyShadowBias(worldPos, normalWS, _LightDirection);
                o.vertex = TransformWorldToHClip(worldPos);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            real4 frag(v2f i) : SV_Target
            {
#if _ALPHATEST_ON
                half4 col = tex2D(_MainTex, i.uv);
                clip(col.a - 0.001);
#endif
                return 0;
            }
            ENDHLSL
        }

CBuffer 定义位置建议

如果你的 shader lab 中各个 pass 使用到的 cbuffer 都最好定义在同一块,而不是每个 pass 中独立定义,这样才能最大限度发挥 SRP Batcher ,也提高 SRP Batcher Compatible 的可能

如:

Shader "xxx"
{
	Properties { ... }
	SubShader
	{
		Pass 
		{
			Name "Body"
            CBUFFER_START(UnityPerMaterial)
                float4 _Params;
            CBUFFER_END
		}
		Pass 
		{
			Name "ShadowCaster"
            CBUFFER_START(UnityPerMaterial)
                float4 _Params;
            CBUFFER_END
		}
	}
}

如上代码,可以看到 Body, ShadowCaster 两个 Pass 中都有 CBUFFER 块的定义那么建议改为:

Shader "xxx"
{
	Properties { ... }
	SubShader
	{
		HLSLINCLUDE
            CBUFFER_START(UnityPerMaterial)
                float4 _Params;
            CBUFFER_END
		ENDHLSL
		Pass 
		{
			Name "Body"
		}
		Pass 
		{
			Name "ShadowCaster"
		}
	}
}

就是把 CBUFFER 块定义在 HLSLINCLUDE ... ENDHLSL 中,也可以定义在独立的 *.hlsl 中,然后 #include "YourCBufferDef.hlsl" 进来也是可以的


会不会浪费 CBUFFER 空间

浪费肯定会有的,但是,你要知道 CBUFFER 是共享的,而且我们这个不是 PerDraw 而是 PerMaterial 的 CBUFFER,几乎可以忽略不计,而且对 SRP Batcher Compatible 的话,通常情况下是有助于性能提升的


Shadow Rerieve

和 Built-in RP 中一样,在渲染提示的 Pass 中,只要添加对 Shadow Map 采样,在做深度比较,然后影响着色亮度即可达到 阴影 效果,下面是主要的渲染实体的 Pass 代码,主要看带有:jave.lin : shadow recieve 的注释部分的代码

        Pass
        {
            HLSLPROGRAM

            //#define _MAIN_LIGHT_SHADOWS
            //#define _SHADOWS_SOFT
            //#define _ALPHATEST_ON
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma shader_feature _ALPHATEST_ON

            #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"
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 shadowCoord : TEXCOORD1; // jave.lin : shadow recieve 在给到 fragment 时,要有阴影坐标
            };

            //CBUFFER_START(UnityPerMaterial)
            //    half4 _Color;
            //    half4 _Color1;
            //    float4 _MainTex_ST;
            //CBUFFER_END

            sampler2D _MainTex;

            v2f vert (appdata v)
            {
                v2f o;
                //o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                //o.vertex = TransformObjectToHClip(v.vertex.xyz);
                float3 worldPos = TransformObjectToWorld(v.vertex.xyz);
                o.vertex = TransformWorldToHClip(worldPos);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.shadowCoord = TransformWorldToShadowCoord(worldPos); // jave.lin : shadow recieve 将 世界坐标 转到 灯光坐标(阴影坐标)
                return o;
            }

            half4 frag(v2f i) : SV_Target
            {
                half3 ambient = half3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w);
                //Light mainLight = GetMainLight(i.shadowCoord); // jave.lin : shadow recieve 获取 shadowAttenuation 衰减值
                //half shadow = mainLight.shadowAttenuation;
                //return shadow;
                //return unity_IndirectSpecColor;
                half shadow = MainLightRealtimeShadow(i.shadowCoord); // jave.lin : shadow recieve 如果不需要用到 Light 结构的数据,可以直接使用该接口来获取
                //real4 ambient = UNITY_LIGHTMODEL_AMBIENT;
                //real4 ambient = glstate_lightmodel_ambient;
                half4 col = tex2D(_MainTex, i.uv);
                half4 finalCol = col * _Color * _Color1;
                // 直接用 ambient 作为阴影色效果不太好
                //finalCol.rgb = lerp(ambient.rgb, finalCol.rgb, shadow);
                // 混合后的效果好很多
                finalCol.rgb = lerp(finalCol.rgb * ambient.rgb, finalCol.rgb, shadow); // jave.lin : shadow recieve 我们可以将 ambient 作为阴影色
                // jave.lin : shadow recieve 部分写法可以是:finalCol.rgb *= shadow; 也是看个人的项目需求来定
                return finalCol;
            }
            ENDHLSL
        }

优化


Shadow Pancaking

阴影花纹(Shadow Pancaking)
引用 cat like coding 的博文中的图:
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第7张图片
处理方式:

            v2f vert(a2v v)
            {
                v2f o = (v2f)0;
                float3 worldPos = TransformObjectToWorld(v.vertex.xyz);
                half3 normalWS = TransformObjectToWorldNormal(v.normal);
                worldPos = ApplyShadowBias(worldPos, normalWS, _LightDirection);
                o.vertex = TransformWorldToHClip(worldPos);
    			// jave.lin : 参考 cat like coding 博主的处理方式
#if UNITY_REVERSED_Z
    			o.vertex.z = min(o.vertex.z, o.vertex.w * UNITY_NEAR_CLIP_VALUE);
#else
    			o.vertex.z = max(o.vertex.z, o.vertex.w * UNITY_NEAR_CLIP_VALUE);
#endif
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

处理后的效果
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第8张图片

理解 cat like coding 博主的这么处理的思路:

    			// jave.lin : 参考 cat like coding 博主的处理方式
#if UNITY_REVERSED_Z
    			o.vertex.z = min(o.vertex.z, o.vertex.w * UNITY_NEAR_CLIP_VALUE);
#else
    			o.vertex.z = max(o.vertex.z, o.vertex.w * UNITY_NEAR_CLIP_VALUE);
#endif

首先:UNITY_NEAR_CLIP_VALUE 单单从字面上理解就是 Unity相机的近截面值

这是里相机最近的有效值

我们知道,如果多边形的某些顶点如果离镜头过近,那么就会被 裁减掉

那么如果我们的 shadow space 空间的 camera 在拍摄某个 opaque 物体时,shadowmap 的内容就会出现镂空的问题,如下图:
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第9张图片

解决它的方法就是:判断如果某些点比近截面还要 近

那么我们就将 这些点压扁到近截面 一样的位置就可以了

如下图:
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第10张图片


Shadow Distance Fade out

阴影的距离淡出

下面的 smoothstep 中 第一个参数是:开始弹出距离,第二个是,结束淡出距离,vz 就是 view space z 的意思

一般可以参数话调整,但是在 URP 中是没有这两个值的,你也可以扩展 URP 的脚本来控制

只要将下面的代码,再 fragment shader 中处理一下就可以了,传入一个 世界坐标位置,所以你可以理解为,将:世界坐标转为 视图坐标,取得 z 值做为距离

下面是在 FS 中计算 VZ 的,可以优化成:在 VS 中计算 VZ,然后作为 V2F 的插值变量传给 FS 即可

float GetDistanceFade(float3 positionWS)
{
    float4 posVS = mul(GetWorldToViewMatrix(), float4(positionWS, 1));
    //return posVS.z;
#if UNITY_REVERSED_Z
    float vz = -posVS.z;
#else
    float vz = posVS.z;
#endif
    // jave.lin : 30.0 : start fade out distance, 40.0 : end fade out distance
    float fade = 1 - smoothstep(30.0, 40.0, vz);
    return fade;
}

应用 fade out 值:

half shadow = MainLightRealtimeShadow(i.shadowCoord); // jave.lin : shadow recieve 如果不需要用到 Light 结构的数据,可以直接使用该接口来获取
half shadowFadeOut = GetDistanceFade(i.positionWS); // jave.lin : 计算 shadow fade out
shadow = lerp(1, shadow, shadowFadeOut); // jave.lin : 阴影 shadow fade out

输出一下 fade out 值,效果如下图:
Unity Shader - URP ShadowCast & ShadowRecieve - 投影 和 接受阴影_第11张图片


提升阴影的掠射角的质量

之前在项目中,发现很多 几何体表面 与 灯光方向 接近平行时,阴影会出现很多瑕疵

优化方法也比较简单,代码如下:

// jave.lin 优化 几何体表面 与 灯光方向 接近平行时,阴影会出现很多瑕疵的问题
half shadow = MainLightRealtimeShadow(i.shadowCoord); // jave.lin : shadow recieve 如果不需要用到 Light 结构的数据,可以直接使用该接口来获取
halfshadowFadeOut = GetDistanceFade(i.positionWS); // jave.lin : 计算 shadow fade out
shadow = lerp(1, shadow, shadowFadeOut); // jave.lin : 阴影 shadow fade out

// 假如你这里有个:diffuse
...
half diffuse = max(dot(N, L));
diffuse = min(diffuse, shadow);

half specular = ...

half factor = diffuse + specular * shadow;

// 这样使用 factor 来控制光阴的瑕疵就会少很多

完整的 Shader

// jave.lin 2021/10/14

Shader "Test/UnlitSTD"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1, 1, 1, 1)
        _Color1 ("Color1", Color) = (1, 1, 1, 1)
        [Toggle] _ALPHATEST ("Alpha Test On", Float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        HLSLINCLUDE
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            CBUFFER_START(UnityPerMaterial)
                half4 _Color;
                half4 _Color1;
                float4 _MainTex_ST;
            CBUFFER_END
        ENDHLSL

        Pass
        {
            HLSLPROGRAM

            //#define _MAIN_LIGHT_SHADOWS
            //#define _SHADOWS_SOFT
            //#define _ALPHATEST_ON
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma shader_feature _ALPHATEST_ON

            #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"
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 shadowCoord : TEXCOORD1; // jave.lin : shadow recieve 在给到 fragment 时,要有阴影坐标
                float3 positionWS : TEXCOORD2;
            };

            //CBUFFER_START(UnityPerMaterial)
            //    half4 _Color;
            //    half4 _Color1;
            //    float4 _MainTex_ST;
            //CBUFFER_END

            sampler2D _MainTex;

            v2f vert (appdata v)
            {
                v2f o;
                //o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                //o.vertex = TransformObjectToHClip(v.vertex.xyz);
                o.positionWS = TransformObjectToWorld(v.vertex.xyz);
                o.vertex = TransformWorldToHClip(o.positionWS);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.shadowCoord = TransformWorldToShadowCoord(o.positionWS); // jave.lin : shadow recieve 将 世界坐标 转到 灯光坐标(阴影坐标)
                return o;
            }
			float GetDistanceFade(float3 positionWS)
			{
			    float4 posVS = mul(GetWorldToViewMatrix(), float4(positionWS, 1));
			    //return posVS.z;
			#if UNITY_REVERSED_Z
			    float vz = -posVS.z;
			#else
			    float vz = posVS.z;
			#endif
			    // jave.lin : 30.0 : start fade out distance, 40.0 : end fade out distance
			    float fade = 1 - smoothstep(30.0, 40.0, vz);
			    return fade;
			}
            half4 frag(v2f i) : SV_Target
            {
                half3 ambient = half3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w);
                //Light mainLight = GetMainLight(i.shadowCoord); // jave.lin : shadow recieve 获取 shadowAttenuation 衰减值
                //half shadow = mainLight.shadowAttenuation;
                //return shadow;
                //return unity_IndirectSpecColor;
                half shadow = MainLightRealtimeShadow(i.shadowCoord); // jave.lin : shadow recieve 如果不需要用到 Light 结构的数据,可以直接使用该接口来获取
                half shadowFadeOut = GetDistanceFade(i.positionWS); // jave.lin : 计算 shadow fade out
                shadow = lerp(1, shadow, shadowFadeOut); // jave.lin : 阴影 shadow fade out
                //real4 ambient = UNITY_LIGHTMODEL_AMBIENT;
                //real4 ambient = glstate_lightmodel_ambient;
                half4 col = tex2D(_MainTex, i.uv);
                half4 finalCol = col * _Color * _Color1;
                // 直接用 ambient 作为阴影色效果不太好
                //finalCol.rgb = lerp(ambient.rgb, finalCol.rgb, shadow);
                // 混合后的效果好很多
                finalCol.rgb = lerp(finalCol.rgb * ambient.rgb, finalCol.rgb, shadow); // jave.lin : shadow recieve 我们可以将 ambient 作为阴影色
                // jave.lin : shadow recieve 部分写法可以是:finalCol.rgb *= shadow; 也是看个人的项目需求来定
                return finalCol;
            }
            ENDHLSL
        }


        Pass // jave.lin : 有 ApplyShadowBias
        {
            Name "ShadowCaster"
            Tags{ "LightMode" = "ShadowCaster" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            struct a2v {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };
            struct v2f {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            // 以下三个 uniform 在 URP shadows.hlsl 相关代码中可以看到没有放到 CBuffer 块中,所以我们只要在 定义为不同的 uniform 即可
            float3 _LightDirection;
            float4 _ShadowBias; // x: depth bias, y: normal bias
            half4 _MainLightShadowParams;  // (x: shadowStrength, y: 1.0 if soft shadows, 0.0 otherwise)
            // jave.lin 直接将:Shadows.hlsl 中的 ApplyShadowBias copy 过来
            float3 ApplyShadowBias(float3 positionWS, float3 normalWS, float3 lightDirection)
            {
                float invNdotL = 1.0 - saturate(dot(lightDirection, normalWS));
                float scale = invNdotL * _ShadowBias.y;
                // normal bias is negative since we want to apply an inset normal offset
                positionWS = lightDirection * _ShadowBias.xxx + positionWS;
                positionWS = normalWS * scale.xxx + positionWS;
                return positionWS;
            }
            v2f vert(a2v v)
            {
                v2f o = (v2f)0;
                float3 worldPos = TransformObjectToWorld(v.vertex.xyz);
                half3 normalWS = TransformObjectToWorldNormal(v.normal);
                worldPos = ApplyShadowBias(worldPos, normalWS, _LightDirection);
                o.vertex = TransformWorldToHClip(worldPos);
    			// jave.lin : 参考 cat like coding 博主的处理方式
#if UNITY_REVERSED_Z
    			o.vertex.z = min(o.vertex.z, o.vertex.w * UNITY_NEAR_CLIP_VALUE);
#else
    			o.vertex.z = max(o.vertex.z, o.vertex.w * UNITY_NEAR_CLIP_VALUE);
#endif
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            real4 frag(v2f i) : SV_Target
            {
#if _ALPHATEST_ON
                half4 col = tex2D(_MainTex, i.uv);
                clip(col.a - 0.001);
#endif
                return 0;
            }
            ENDHLSL
        }

//        Pass // jave.lin : 没有 ApplyShadowBias
//        {
//            Name "ShadowCaster"
//            Tags{ "LightMode" = "ShadowCaster" }
//            HLSLPROGRAM
//            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
//            #pragma vertex vert
//            #pragma fragment frag
//            #pragma shader_feature _ALPHATEST_ON
//            // jave.lin : 根据你的 alpha test 是否开启而定
//            //#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
//            struct a2v {
//                float4 vertex : POSITION;
//                float2 uv : TEXCOORD0;
//            };
//            struct v2f {
//                float4 vertex : SV_POSITION;
//                float2 uv : TEXCOORD0;
//            };
//            v2f vert(a2v v)
//            {
//                v2f o = (v2f)0;
//                o.vertex = TransformObjectToHClip(v.vertex.xyz);
//                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//                return o;
//            }
//            real4 frag(v2f i) : SV_Target
//            {
//#if _ALPHATEST_ON
//                half4 col = tex2D(_MainTex, i.uv);
//                clip(col.a - 0.001);
//#endif
//                return 0;
//            }
//            ENDHLSL
//        }

        // jave.lin : 使用 Universal 中自带的 Universal Render Pipeline/Lit Shader 中的 ShadowCaster Pass
        //UsePass "Universal Render Pipeline/Lit/ShadowCaster"
    }
}


References

  • 关于SHADOWS_SCREEN - URP 下的阴影
  • 如何在unity的URP下实现阴影
  • Unity URP Shader 支持内置阴影

Extended reading

  • Unity通用渲染管线(URP)系列(四)——方向阴影(Cascaded Shadow Maps) - 此篇引用的是 国外 cat like coding 博主的文章,有非常详细的讲解

你可能感兴趣的:(unity,unity-shader,unity,unity,URP,阴影,URP,投影,和,接受阴影)