GameTech/URP 渲染实战
源码 / Ch21/Flowmap.shader
Ch21/Flowmap.shader
// 案例 21.3:Flowmap——用一张“流向图”驱动贴图沿任意方向流动,双相位消除跳变
Shader "Cookbook/Ch21/Flowmap"
{
    Properties
    {
        _BaseMap ("Base", 2D) = "white" {}
        [NoScaleOffset] _FlowMap ("Flow Map (RG)", 2D) = "gray" {}
        _FlowStrength ("Flow Strength", Range(0, 1)) = 0.25
        _Speed ("Cycle Speed", Float) = 0.5
        _Phase ("Phase (for stills)", Float) = 0
        [Enum(ShowFlow,0,SinglePhase,1,DualPhase,2)] _Mode ("Mode", Float) = 2
        [Toggle] _UseRamp ("Use Ramp (lava look)", Float) = 0
        [NoScaleOffset] _RampTex ("Ramp", 2D) = "white" {}
        _Intensity ("Ramp Intensity", Float) = 2
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        Pass
        {
            Name "Unlit"
            Tags { "LightMode"="UniversalForwardOnly" }   // 自定义表面混合,没有 GBuffer Pass:Deferred 下也走前向
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #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;
                half _FlowStrength;
                float _Speed, _Phase;
                half _Mode, _UseRamp, _Intensity;
            CBUFFER_END
            TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
            TEXTURE2D(_FlowMap); SAMPLER(sampler_FlowMap);
            TEXTURE2D(_RampTex); SAMPLER(sampler_RampTex);

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

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

            half4 frag(Varyings i) : SV_Target
            {
                half4 flowTex = SAMPLE_TEXTURE2D(_FlowMap, sampler_FlowMap, i.uv);
                if (_Mode < 0.5) return half4(flowTex.rg, 0, 1);          // 直接显示流向图

                // RG 存的是 0..1,映射回 -1..1 的方向向量(0.5 灰 = 静止)
                float2 flow = (flowTex.rg * 2.0 - 1.0) * _FlowStrength;
                float t = (_Time.y + _Phase) * _Speed;
                float2 uv = TRANSFORM_TEX(i.uv, _BaseMap);

                half4 c;
                if (_Mode < 1.5)
                {
                    // 单相位:UV 沿流向越漂越远,到 frac 归零时整张图“啪”地跳回原位
                    c = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv - flow * frac(t));
                }
                else
                {
                    FlowSample fs = FlowUV(uv, flow, t);
                    half4 c0 = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, fs.uv0);
                    half4 c1 = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, fs.uv1);
                    c = c0 * fs.w0 + c1 * (1.0 - fs.w0);
                }

                if (_UseRamp > 0.5)
                {
                    half k = smoothstep(0.2, 0.9, c.r);
                    c.rgb = SAMPLE_TEXTURE2D(_RampTex, sampler_RampTex, float2(k, 0.5)).rgb * (k * k * _Intensity);
                }
                return half4(c.rgb, 1);
            }
            ENDHLSL
        }
    }
}