GameTech/URP 渲染实战
源码 / Ch04/C04_06_Voronoi.shader
Ch04/C04_06_Voronoi.shader
// 案例 4.6:Voronoi 细胞 —— F1 距离、F2-F1 边界、按格子 id 上色。
Shader "Cookbook/Ch04/Voronoi"
{
    Properties
    {
        [Enum(F1,0,Edges,1,Cells,2)] _Mode ("Mode", Float) = 2
        _Scale ("Scale", Float) = 6
        _Phase ("Phase (for stills)", Float) = 0
        _Speed ("Animate Speed", Float) = 1
        _EdgeWidth ("Edge Width", Range(0.001, 0.3)) = 0.06
        _ColorA ("Color A", Color) = (0.12, 0.42, 0.56, 1)
        _ColorB ("Color B", Color) = (0.95, 0.80, 0.55, 1)
        _EdgeColor ("Edge Color", Color) = (0.08, 0.09, 0.11, 1)
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

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

            HLSLPROGRAM
            #pragma vertex ProceduralVert
            #pragma fragment frag
            #include "Assets/Cookbook/Ch04/ProceduralCommon.hlsl"

            CBUFFER_START(UnityPerMaterial)
                float _Mode, _Scale, _Phase, _Speed, _EdgeWidth;
                half4 _ColorA, _ColorB, _EdgeColor;
            CBUFFER_END

            half4 frag (Varyings input) : SV_Target
            {
                float2 p = input.uv * _Scale;
                float f1, f2; float2 cell;
                Voronoi(p, _Phase + _Time.y * _Speed, f1, f2, cell);

                half3 col;
                if (_Mode < 0.5)
                {
                    // F1:到最近特征点的距离 → 一个个“圆锥”,加等高线更容易看出形状
                    col = lerp(_ColorA.rgb, _ColorB.rgb, saturate(f1 * 1.4));
                    col *= 0.85 + 0.15 * step(0.5, frac(f1 * 8.0));
                    col = lerp(col, _EdgeColor.rgb, 1.0 - smoothstep(0.03, 0.05, f1));   // 特征点
                }
                else if (_Mode < 1.5)
                {
                    // F2 - F1:两个最近点距离相等的地方就是细胞边界,那里差值趋于 0
                    float e = f2 - f1;
                    col = lerp(_EdgeColor.rgb, _ColorB.rgb, smoothstep(0.0, _EdgeWidth * 2.0, e));
                }
                else
                {
                    // 用最近格子的编号再哈希一次,每个细胞一个固定颜色
                    float h = Hash21(cell + 31.7);
                    col = lerp(_ColorA.rgb, _ColorB.rgb, h);
                    // 用到边界的真实距离做明暗和描边:中心亮、边缘暗,像鹅卵石
                    float border = VoronoiBorder(p, _Phase + _Time.y * _Speed);
                    col *= lerp(0.7, 1.1, saturate(border * 2.5));
                    float aa = fwidth(border);
                    col = lerp(_EdgeColor.rgb, col, smoothstep(_EdgeWidth * 0.5 - aa, _EdgeWidth * 0.5 + aa, border));
                }
                return half4(col, 1);
            }
            ENDHLSL
        }
    }
}