GameTech/URP 渲染实战
源码 / shaders/Ch17/VAT.glsl
shaders/Ch17/VAT.glsl
// 播放 VAT:顶点用 uv1.x(自己的编号)+ 当前帧号去查位置/法线贴图,相邻两帧之间线性插值。(GLSL 版)
// 帧号 = _Time.y * _FPS + _Frame;静帧时 _Time≈0,直接用 _Frame 指定。
Shader "Cookbook/Ch17/VAT"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        GLSLINCLUDE
        uniform float _FPS, _Frame;
        uniform vec4 _RootColor, _TipColor;
        uniform vec4 _PosTex_TexelSize;      // (1/宽, 1/高, 宽, 高):高 = 帧数
        uniform highp sampler2D _PosTex;
        uniform highp sampler2D _NrmTex;
        #include "Ch17/VertexAnimCommon.glsl"

        // HLSL 的 fmod:结果与被除数同号(GLSL 的 mod 对负数不同)
        float fmodHLSL(float a, float b) { return a - b * trunc(a / b); }

        // GLSL 不能按调用指定采样器(sampler_PointClamp):点采样直接用 texelFetch 取纹素
        vec4 SamplePoint(highp sampler2D tex, vec2 uv)
        {
            ivec2 size = textureSize(tex, 0);
            ivec2 p = clamp(ivec2(uv * vec2(size)), ivec2(0), size - 1);
            return texelFetch(tex, p, 0);
        }

        void Deform(inout VAAttributes v)
        {
            float frames = _PosTex_TexelSize.w;
            float f = fmodHLSL(_Time.y * _FPS + _Frame, frames);
            float f0 = floor(f);
            float f1 = fmodHLSL(f0 + 1.0, frames);   // 最后一帧插值回第一帧,循环无缝
            float w = f - f0;

            // 采样纹素中心;必须用点采样 + LOD 0,否则相邻顶点/相邻帧会被双线性混在一起
            vec2 uv0 = vec2(v.uv1.x, (f0 + 0.5) / frames);
            vec2 uv1 = vec2(v.uv1.x, (f1 + 0.5) / frames);
            vec3 p0 = SamplePoint(_PosTex, uv0).xyz;
            vec3 p1 = SamplePoint(_PosTex, uv1).xyz;
            vec3 n0 = SamplePoint(_NrmTex, uv0).xyz;
            vec3 n1 = SamplePoint(_NrmTex, uv1).xyz;

            v.positionOS.xyz = mix(p0, p1, w);       // 贴图里存的是绝对位置,直接替换
            v.normalOS = normalize(mix(n0, n1, w));
        }

        #include "Ch17/VertexAnimPasses.glsl"
        ENDGLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            GLSLPROGRAM
            #ifdef VERTEX
            void main() { VAForwardVert(); }
            #endif
            #ifdef FRAGMENT
            void main()
            {
                vec3 albedo = mix(_RootColor.rgb, _TipColor.rgb, uv.y);
                // 横向的环纹,方便看出扭转
                albedo *= 0.8 + 0.2 * step(0.5, fract(uv.x * 4.0));
                fragColor = vec4(VALighting(albedo, normalize(normalWS), positionWS, 0.6, 48.0), 1.0);
            }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            GLSLPROGRAM
            #ifdef VERTEX
            void main() { VAShadowVert(); }
            #endif
            #ifdef FRAGMENT
            void main() { VANullFrag(); }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            GLSLPROGRAM
            #ifdef VERTEX
            void main() { VADepthVert(); }
            #endif
            #ifdef FRAGMENT
            void main() { VANullFrag(); }
            #endif
            ENDGLSL
        }
    }
}