GameTech/URP 渲染实战
源码 / Ch24/SGDissolveEquivalent.shader
Ch24/SGDissolveEquivalent.shader
// 第 24 章:与“Lit Graph 溶解”等价的手写 Shader。
// Lit Graph 的 Master Stack 输出 Base Color / Metallic / Smoothness / Emission / Alpha / Alpha Clip Threshold,
// 生成代码最终把它们填进 SurfaceData 再调用 UniversalFragmentPBR —— 这里照做一遍。
Shader "Cookbook/Ch24/SGDissolveEquivalent"
{
    Properties
    {
        _BaseColor ("Base Color", Color) = (0.75, 0.78, 0.85, 1)
        _Metallic ("Metallic", Range(0, 1)) = 0.6
        _Smoothness ("Smoothness", Range(0, 1)) = 0.55
        _NoiseMap ("Noise", 2D) = "gray" {}
        _Progress ("Progress", Range(0, 1)) = 0.4
        _EdgeWidth ("Edge Width", Range(0.001, 0.3)) = 0.08
        [HDR] _EdgeColor ("Edge Color", Color) = (4, 1.4, 0.3, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="AlphaTest" }

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Assets/Cookbook/Ch24/StylizedFunctions.hlsl"
        CBUFFER_START(UnityPerMaterial)
            half4  _BaseColor;
            half   _Metallic;
            half   _Smoothness;
            float4 _NoiseMap_ST;
            half   _Progress;
            half   _EdgeWidth;
            half4  _EdgeColor;
        CBUFFER_END
        TEXTURE2D(_NoiseMap); SAMPLER(sampler_NoiseMap);

        // 图里“Sample Texture 2D → Dissolve”这一段,所有 Pass 共用
        void DissolveGraph(float2 uv, out float alpha, out float edge)
        {
            float noise = SAMPLE_TEXTURE2D(_NoiseMap, sampler_NoiseMap, TRANSFORM_TEX(uv, _NoiseMap)).r;
            Dissolve_float(noise, _Progress, _EdgeWidth, alpha, edge);
        }
        ENDHLSL

        Pass
        {
            Name "Universal Forward"
            Tags { "LightMode"="UniversalForward" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
            #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
            #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
            #pragma multi_compile _ _FORWARD_PLUS
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.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 normalWS : TEXCOORD2; };

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

            half4 frag(Varyings i) : SV_Target
            {
                float alpha, edge;
                DissolveGraph(i.uv, alpha, edge);
                clip(alpha - 0.5);                         // Alpha Clip Threshold = 0.5

                SurfaceData surface = (SurfaceData)0;      // ← Master Stack 的各个 Block
                surface.albedo     = _BaseColor.rgb * (1 - edge);
                surface.metallic   = _Metallic;
                surface.smoothness = _Smoothness;
                surface.occlusion  = 1;
                surface.emission   = edge * _EdgeColor.rgb;
                surface.alpha      = 1;
                surface.normalTS   = half3(0, 0, 1);

                InputData input = (InputData)0;
                input.positionWS = i.positionWS;
                input.normalWS = normalize(i.normalWS);
                input.viewDirectionWS = GetWorldSpaceNormalizeViewDir(i.positionWS);
                input.shadowCoord = TransformWorldToShadowCoord(i.positionWS);
                input.bakedGI = SampleSH(input.normalWS);
                input.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(i.positionCS);
                input.shadowMask = half4(1, 1, 1, 1);
                return UniversalFragmentPBR(input, surface);
            }
            ENDHLSL
        }

        Pass
        {
            // Shader Graph 会自动把 Alpha Clip 带进 ShadowCaster / DepthOnly;手写时最容易忘
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
            #include "Assets/Cookbook/Ch23/Ch23Common.hlsl"
            struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; };
            struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; };
            Varyings vert(Attributes v)
            {
                Varyings o;
                CasterAttributes c; c.positionOS = v.positionOS; c.normalOS = v.normalOS;
                o.positionCS = Ch23ShadowCasterVert(c);
                o.uv = v.uv;
                return o;
            }
            half4 frag(Varyings i) : SV_Target
            {
                float alpha, edge;
                DissolveGraph(i.uv, alpha, edge);
                clip(alpha - 0.5);
                return 0;
            }
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; };
            struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; };
            Varyings vert(Attributes v) { Varyings o; o.positionCS = TransformObjectToHClip(v.positionOS.xyz); o.uv = v.uv; return o; }
            half4 frag(Varyings i) : SV_Target
            {
                float alpha, edge;
                DissolveGraph(i.uv, alpha, edge);
                clip(alpha - 0.5);
                return 0;
            }
            ENDHLSL
        }
    }
}