GameTech/URP 渲染实战
源码 / shaders/Ch18/Frozen.glsl
shaders/Ch18/Frozen.glsl
// 案例 18.9:冰冻 / 结晶覆盖。世界空间“自上而下”覆盖一层冰:加厚、冰裂纹、高光滑度、冷色边缘与闪点。
// (GLSL 版,对应 Ch18/Frozen.shader)
Shader "Cookbook/Ch18/Frozen"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" "RenderPipeline"="UniversalPipeline" }

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForward" }

            GLSLPROGRAM
            #include "Ch18/FrozenCore.glsl"
            #include "Ch18/Ch18Common.glsl"

            VARYING vec3 positionWS;
            VARYING vec3 normalWS;
            VARYING vec2 uv;
            VARYING vec3 restWS;   // 加厚之前的位置,用来采样噪声,避免纹理随厚度“游动”

            #ifdef VERTEX
            void main()
            {
                vec3 pWS = TransformObjectToWorld(positionOS.xyz);
                vec3 nWS = TransformObjectToWorldNormal(normalOS);
                restWS = pWS;
                positionWS = ThickenIce(pWS, nWS);
                gl_Position = TransformWorldToHClip(positionWS);
                normalWS = nWS;
                uv = TransformTex(texcoord0.xy, _BaseMap_ST);
            }
            #endif

            #ifdef FRAGMENT
            void main()
            {
                vec3 N = normalize(normalWS);
                vec3 viewWS = GetWorldSpaceNormalizeViewDir(positionWS);
                float mask = FrostMask(restWS, N);

                vec3 baseAlbedo = texture(_BaseMap, uv).rgb * _BaseColor.rgb;

                // 冰的颜色:噪声在浅色与深色之间变化,Worley 的细胞边界做成白色裂纹
                float n = TriplanarNoise(_IceNoise, restWS, N, _IceNoiseScale * 3.0);
                float cell = TriplanarNoise(_CrackMap, restWS, N, _CrackScale);
                float crack = smoothstep(0.55, 0.8, cell) * _CrackStrength;
                vec3 ice = mix(_IceDeepColor.rgb, _IceColor.rgb, saturate(n * 1.2 + crack));
                // 冰层很薄的地方(遮罩边缘)透出一点底色
                vec3 albedo = mix(baseAlbedo, ice, mask * (0.75 + 0.25 * mask));
                float smooth_ = mix(_Smoothness, 0.9, mask);

                vec3 color = SimpleLighting(albedo, positionWS, N, smooth_);

                // 冷色菲涅尔:冰面边缘泛蓝白,模拟光在冰层里散射后从边缘透出来
                float rim = FresnelRim(N, viewWS, 3.0);
                color += _IceRimColor.rgb * rim * mask;

                // 闪点:极高频哈希,只保留最亮的极少数点;乘上视角相关的项,转动相机时会“闪”
                vec3 sp = floor(restWS * 160.0);
                float sparkle = step(0.997, Hash21(sp.xz + sp.y * 17.0));
                vec3 halfDir = SafeNormalize(GetMainLight().direction + viewWS);
                sparkle *= pow(saturate(dot(N, halfDir)), 8.0);
                color += sparkle * _SparkleStrength * mask * GetMainLight().color;
                fragColor = vec4(color, 1.0);
            }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            GLSLPROGRAM
            #include "Ch18/FrozenCore.glsl"
            #ifdef VERTEX
            void main()
            {
                vec3 positionWS = TransformObjectToWorld(positionOS.xyz);
                vec3 normalWS = TransformObjectToWorldNormal(normalOS);
                positionWS = ThickenIce(positionWS, normalWS);
                vec3 lightDirWS = _CastingPunctualLightShadow > 0.5 ? normalize(_LightPosition - positionWS) : _LightDirection;
                vec4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirWS));
                positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE * positionCS.w);
                gl_Position = positionCS;
            }
            #endif
            #ifdef FRAGMENT
            void main() { fragColor = vec4(0.0); }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            GLSLPROGRAM
            #include "Ch18/FrozenCore.glsl"
            #ifdef VERTEX
            void main()
            {
                vec3 positionWS = TransformObjectToWorld(positionOS.xyz);
                gl_Position = TransformWorldToHClip(ThickenIce(positionWS, TransformObjectToWorldNormal(normalOS)));
            }
            #endif
            #ifdef FRAGMENT
            void main() { fragColor = vec4(gl_FragCoord.z); }
            #endif
            ENDGLSL
        }
    }
}