GameTech/URP 渲染实战
源码 / shaders/Ch04/C04_03_HealthBar.glsl
shaders/Ch04/C04_03_HealthBar.glsl
// 案例 4.3(下):分段血条 —— 圆角矩形 SDF、受伤拖尾、低血量闪烁。(GLSL 版)
Shader "Cookbook/Ch04/HealthBar"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }

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

            GLSLPROGRAM
            #include "Ch04/ProceduralCommon.glsl"

            uniform float _Health, _Trail, _Segments, _Aspect, _LowThreshold, _FlashSpeed, _FlashPhase;
            uniform vec4  _ColorFull, _ColorLow, _TrailColor, _FrameColor, _EmptyColor, _BgColor;

            #ifdef FRAGMENT
            void main()
            {
                // 乘以宽高比,让 SDF 在拉长的面片上仍然是“正”的(圆角不会被压扁)
                vec2 p = (uv - 0.5) * vec2(_Aspect, 1.0);
                vec2 halfSize = vec2(_Aspect * 0.5, 0.5);
                float outer = SdRoundBox(p, halfSize - 0.04, 0.22);     // 外框
                float inner = SdRoundBox(p, halfSize - 0.14, 0.13);     // 槽
                // 槽内的横向进度 0..1
                float innerHalfW = halfSize.x - 0.14;
                float x01 = saturate((p.x + innerHalfW) / (2.0 * innerHalfW));

                // 血量颜色:低于阈值后快速变红
                vec3 hpCol = mix(_ColorLow.rgb, _ColorFull.rgb, smoothstep(_LowThreshold * 0.6, _LowThreshold * 1.6, _Health));
                // 低血量闪烁:_Time 驱动;静帧时用 _FlashPhase
                float flash = 0.5 + 0.5 * sin(_Time.y * _FlashSpeed + _FlashPhase);
                hpCol *= (_Health < _LowThreshold) ? mix(0.7, 1.4, flash) : 1.0;

                float px = fwidth(x01);
                float hpMask    = 1.0 - smoothstep(_Health - px, _Health + px, x01);
                float trailMask = 1.0 - smoothstep(_Trail  - px, _Trail  + px, x01);

                vec3 col = _EmptyColor.rgb;
                col = mix(col, _TrailColor.rgb, trailMask);            // 先画拖尾,再盖上当前血量
                col = mix(col, hpCol, hpMask);
                // 上亮下暗,做出一点体积感
                col *= mix(0.75, 1.15, smoothstep(-0.35, 0.35, p.y));
                // 分段刻度:fract 重复出竖线
                float seg = abs(fract(x01 * _Segments + 0.5) - 0.5) / _Segments;   // 到最近刻度的距离(x01 单位)
                col *= mix(0.55, 1.0, smoothstep(px * 0.5, px * 1.5, seg));

                vec3 outCol = mix(_BgColor.rgb, _FrameColor.rgb, FillAA(outer));
                outCol = mix(outCol, col, FillAA(inner));
                fragColor = vec4(outCol, 1.0);
            }
            #endif
            ENDGLSL
        }
    }
}