GameTech/URP 渲染实战
源码 / Ch17/VAT.shader
Ch17/VAT.shader
// 播放 VAT:顶点用 uv1.x(自己的编号)+ 当前帧号去查位置/法线贴图,相邻两帧之间线性插值。
// 帧号 = _Time.y * _FPS + _Frame;静帧时 _Time≈0,直接用 _Frame 指定。
Shader "Cookbook/Ch17/VAT"
{
    Properties
    {
        [NoScaleOffset] _PosTex ("VAT Position", 2D) = "black" {}
        [NoScaleOffset] _NrmTex ("VAT Normal", 2D) = "bump" {}
        _FPS ("Frames Per Second", Float) = 24
        _Frame ("Frame Offset", Float) = 0
        _RootColor ("Root Color", Color) = (0.45, 0.12, 0.35, 1)
        _TipColor ("Tip Color", Color) = (1, 0.55, 0.45, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        CBUFFER_START(UnityPerMaterial)
            float _FPS, _Frame;
            half4 _RootColor, _TipColor;
            float4 _PosTex_TexelSize;      // (1/宽, 1/高, 宽, 高):高 = 帧数
        CBUFFER_END
        TEXTURE2D(_PosTex);
        TEXTURE2D(_NrmTex);
        #include "Assets/Cookbook/Ch17/VertexAnimCommon.hlsl"

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

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

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

        #include "Assets/Cookbook/Ch17/VertexAnimPasses.hlsl"
        ENDHLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }
            HLSLPROGRAM
            #pragma vertex VAForwardVert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
            #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
            half4 frag(VAVaryings i) : SV_Target
            {
                half3 albedo = lerp(_RootColor.rgb, _TipColor.rgb, i.uv.y);
                // 横向的环纹,方便看出扭转
                albedo *= 0.8h + 0.2h * step(0.5h, frac(i.uv.x * 4.0h));
                return half4(VALighting(albedo, normalize(i.normalWS), i.positionWS, 0.6h, 48.0h), 1);
            }
            ENDHLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0
            HLSLPROGRAM
            #pragma vertex VAShadowVert
            #pragma fragment VANullFrag
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            HLSLPROGRAM
            #pragma vertex VADepthVert
            #pragma fragment VANullFrag
            ENDHLSL
        }
    }
}