GameTech/URP 渲染实战
源码 / Ch18/Dissolve.shader
Ch18/Dissolve.shader
// 案例 18.1~18.3:溶解。噪声 clip + 边缘发光 + 三种溶解形状(噪声 / 方向 / 球形)。
Shader "Cookbook/Ch18/Dissolve"
{
    Properties
    {
        [MainTexture] _BaseMap ("Base Map", 2D) = "white" {}
        [MainColor] _BaseColor ("Base Color", Color) = (1,1,1,1)
        _Smoothness ("Smoothness", Range(0,1)) = 0.4
        [Header(Dissolve)]
        [NoScaleOffset] _NoiseMap ("Noise (R)", 2D) = "gray" {}
        _NoiseMap_ST ("Noise Tiling/Offset", Vector) = (1,1,0,0)
        _Progress ("Progress", Range(0,1)) = 0.3
        [Toggle] _AutoPlay ("Auto Play (_Time)", Float) = 0
        [KeywordEnum(Noise, Direction, Sphere)] _Mode ("Mode", Float) = 0
        _NoiseStrength ("Noise Strength (Dir/Sphere)", Range(0,1)) = 0.25
        _DissolveDir ("Direction (Object)", Vector) = (0,1,0,0)
        _DirRange ("Direction Range (min,max)", Vector) = (-0.5,0.5,0,0)
        _SphereCenter ("Sphere Center(xyz) MaxRadius(w)", Vector) = (0,0,0,2)
        [Header(Edge)]
        _EdgeWidth ("Edge Width", Range(0,0.3)) = 0.08
        [HDR] _EdgeColor ("Edge Color", Color) = (4,1.6,0.4,1)
        [Toggle] _UseRamp ("Use Ramp", Float) = 1
        [NoScaleOffset] _EdgeRamp ("Edge Ramp", 2D) = "white" {}
        [Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 0
    }

    SubShader
    {
        // 溶解本质是 Alpha Test:用 AlphaTest 队列,让 Early-Z 友好的不透明物体先画
        Tags { "RenderType"="TransparentCutout" "Queue"="AlphaTest" "RenderPipeline"="UniversalPipeline" }

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForward" }
            Cull [_Cull]

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma shader_feature_local _MODE_NOISE _MODE_DIRECTION _MODE_SPHERE
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
            #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
            #pragma multi_compile _ _FORWARD_PLUS
            #pragma multi_compile_fog

            #include "Assets/Cookbook/Ch18/DissolveCore.hlsl"
            #include "Assets/Cookbook/Ch18/Ch18Common.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS   : NORMAL;
                float2 uv         : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv         : TEXCOORD0;
                float3 positionWS : TEXCOORD1;
                float3 positionOS : TEXCOORD2;
                half3  normalWS   : TEXCOORD3;
                half   fogFactor  : TEXCOORD4;
            };

            Varyings vert(Attributes v)
            {
                Varyings o;
                VertexPositionInputs p = GetVertexPositionInputs(v.positionOS.xyz);
                o.positionCS = p.positionCS;
                o.positionWS = p.positionWS;
                o.positionOS = v.positionOS.xyz;
                o.normalWS = TransformObjectToWorldNormal(v.normalOS);
                o.uv = v.uv;
                o.fogFactor = ComputeFogFactor(p.positionCS.z);
                return o;
            }

            half4 frag(Varyings i, FRONT_FACE_TYPE face : FRONT_FACE_SEMANTIC) : SV_Target
            {
                half edge;
                DissolveClip(DissolveValue(i.uv, i.positionOS, i.positionWS), edge);

                half3 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv * _BaseMap_ST.xy + _BaseMap_ST.zw).rgb * _BaseColor.rgb;
                // 双面渲染时,背面(物体内壁)把法线翻过来,再压暗一点,看起来像“空心的内部”
                half3 normalWS = normalize(i.normalWS) * IS_FRONT_VFACE(face, 1.0h, -1.0h);
                albedo *= IS_FRONT_VFACE(face, 1.0h, 0.35h);

                // 前沿附近先“烧焦”变暗,再在最前沿发光
                albedo *= 1.0h - 0.85h * smoothstep(0.0h, 0.6h, edge);
                half3 color = SimpleLighting(albedo, i.positionWS, normalWS, _Smoothness);

                half3 glow = _EdgeColor.rgb;
                if (_UseRamp > 0.5)
                    glow *= SAMPLE_TEXTURE2D(_EdgeRamp, sampler_EdgeRamp, float2(edge, 0.5)).rgb;
                // edge^2:让发光集中在最前沿,向内快速衰减
                color += glow * edge * edge;

                color = MixFog(color, i.fogFactor);
                return half4(color, 1);
            }
            ENDHLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            Cull [_Cull]

            HLSLPROGRAM
            #pragma vertex vertShadow
            #pragma fragment fragClip
            #pragma shader_feature_local _MODE_NOISE _MODE_DIRECTION _MODE_SPHERE
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
            #include "Assets/Cookbook/Ch18/DissolveCore.hlsl"
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"

            float3 _LightDirection;   // 由 URP 的 ShadowUtils 在画阴影前设置
            float3 _LightPosition;

            struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; };
            struct Varyings  { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; float3 positionOS : TEXCOORD1; float3 positionWS : TEXCOORD2; };

            Varyings vertShadow(Attributes v)
            {
                Varyings o;
                float3 positionWS = TransformObjectToWorld(v.positionOS.xyz);
                float3 normalWS = TransformObjectToWorldNormal(v.normalOS);
            #if _CASTING_PUNCTUAL_LIGHT_SHADOW
                float3 lightDirWS = normalize(_LightPosition - positionWS);
            #else
                float3 lightDirWS = _LightDirection;
            #endif
                o.positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirWS));
            #if UNITY_REVERSED_Z
                o.positionCS.z = min(o.positionCS.z, UNITY_NEAR_CLIP_VALUE);
            #else
                o.positionCS.z = max(o.positionCS.z, UNITY_NEAR_CLIP_VALUE);
            #endif
                o.uv = v.uv; o.positionOS = v.positionOS.xyz; o.positionWS = positionWS;
                return o;
            }

            half4 fragClip(Varyings i) : SV_Target
            {
                half edge;
                DissolveClip(DissolveValue(i.uv, i.positionOS, i.positionWS), edge);
                return 0;
            }
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            Cull [_Cull]

            HLSLPROGRAM
            #pragma vertex vertDepth
            #pragma fragment fragClip
            #pragma shader_feature_local _MODE_NOISE _MODE_DIRECTION _MODE_SPHERE
            #include "Assets/Cookbook/Ch18/DissolveCore.hlsl"

            struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; };
            struct Varyings  { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; float3 positionOS : TEXCOORD1; float3 positionWS : TEXCOORD2; };

            Varyings vertDepth(Attributes v)
            {
                Varyings o;
                o.positionWS = TransformObjectToWorld(v.positionOS.xyz);
                o.positionCS = TransformWorldToHClip(o.positionWS);
                o.uv = v.uv; o.positionOS = v.positionOS.xyz;
                return o;
            }

            half4 fragClip(Varyings i) : SV_Target
            {
                half edge;
                DissolveClip(DissolveValue(i.uv, i.positionOS, i.positionWS), edge);
                return i.positionCS.z;
            }
            ENDHLSL
        }
    }
}