GameTech/URP 渲染实战
源码 / Ch21/Flipbook.shader
Ch21/Flipbook.shader
// 案例 21.2:序列帧动画(Flipbook)——按时间算帧号,可选帧间混合;附带图集调试视图
Shader "Cookbook/Ch21/Flipbook"
{
    Properties
    {
        [MainTexture] _BaseMap ("Flipbook Atlas", 2D) = "white" {}
        [HDR] _BaseColor ("Tint", Color) = (1,1,1,1)
        _Grid ("Columns(x) Rows(y)", Vector) = (4, 4, 0, 0)
        _FPS ("Frames Per Second", Float) = 12
        _Frame ("Frame Offset (for stills)", Float) = 0
        [Toggle(_FLIPBOOK_BLEND)] _Blend ("Blend Between Frames", Float) = 1
        [Toggle(_DEBUG_ATLAS)] _DebugAtlas ("Debug: Show Atlas", Float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "RenderPipeline"="UniversalPipeline" "Queue"="Transparent" "IgnoreProjector"="True" }

        Pass
        {
            Name "Unlit"
            Tags { "LightMode"="UniversalForward" }
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off
            Cull Off
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma shader_feature_local_fragment _FLIPBOOK_BLEND
            #pragma shader_feature_local_fragment _DEBUG_ATLAS
            #pragma multi_compile_instancing
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Assets/Cookbook/Ch21/UVAnim.hlsl"

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                half4 _BaseColor;
                float4 _Grid;
                float _FPS, _Frame;
            CBUFFER_END
            TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);

            struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; UNITY_VERTEX_INPUT_INSTANCE_ID };
            struct Varyings  { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; };

            Varyings vert(Attributes v)
            {
                Varyings o;
                UNITY_SETUP_INSTANCE_ID(v);
                o.positionCS = TransformObjectToHClip(v.positionOS.xyz);
                o.uv = v.uv;
                o.color = v.color;
                return o;
            }

            half4 frag(Varyings i) : SV_Target
            {
                float frame = _Time.y * _FPS + _Frame;       // 连续的“帧号”,小数部分就是两帧之间的进度

            #if defined(_DEBUG_ATLAS)
                // 调试:整张图集 + 网格线;当前帧描橙框,下一帧描蓝框
                half4 atlas = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv);
                half3 bg = lerp(half3(0.12, 0.16, 0.2), atlas.rgb, atlas.a);
                float2 cell = i.uv * _Grid.xy;
                float2 f = frac(cell);
                float line_ = step(min(min(f.x, 1 - f.x), min(f.y, 1 - f.y)), 0.015);
                float idx = (_Grid.y - 1 - floor(cell.y)) * _Grid.x + floor(cell.x);
                float total = _Grid.x * _Grid.y;
                float cur = fmod(floor(frame), total), nxt = fmod(floor(frame) + 1, total);
                float border = step(min(min(f.x, 1 - f.x), min(f.y, 1 - f.y)), 0.05);
                bg = lerp(bg, half3(0.5, 0.55, 0.6), line_);
                bg = lerp(bg, half3(1.0, 0.45, 0.15), border * (idx == cur));
                bg = lerp(bg, half3(0.25, 0.6, 1.0), border * (idx == nxt));
                return half4(bg, 1);
            #endif

                float2 uvA = FlipbookUV(i.uv, frame, _Grid.xy);
                half4 c = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uvA);
            #if defined(_FLIPBOOK_BLEND)
                // 帧间混合:再采一张“下一帧”,按小数部分线性插值。代价是多一次采样
                float2 uvB = FlipbookUV(i.uv, frame + 1.0, _Grid.xy);
                half4 cB = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uvB);
                c = lerp(c, cB, frac(frame));
            #endif
                c *= _BaseColor * i.color;
                return c;
            }
            ENDHLSL
        }
    }
}