Shader "IndianOcean/SpriteEchoOutline"
{
    // 回声描边 Shader —— 配合 EchoSystem 使用
    // 渲染精灵的"外描边"，颜色默认白色（可自定义），由全局回声参数控制显隐。
    // 渲染队列在黑暗遮罩（Transparent+100）之上（Transparent+200），
    // 所以即使在黑暗中（黑暗遮罩把场景乘成纯黑），描边依然可见。
    //
    // 工作原理：
    // - 顶点着色器：以精灵中心为基准向外扩展顶点，为外描边腾出空间
    // - 片元着色器：8 方向膨胀采样精灵 alpha，邻居有 alpha 但中心没有 → 描边像素
    // - 回声控制：精灵世界坐标距 _EchoCenter 小于 _EchoRadius 才显示描边
    Properties
    {
        _MainTex ("Sprite Texture", 2D) = "white" {}
        _OutlineColor ("描边颜色（乘以全局回声颜色，默认白）", Color) = (1,1,1,1)
        _OutlineWidth ("描边宽度（像素/纹素）", Range(0, 8)) = 2.0
    }
    SubShader
    {
        Tags
        {
            "RenderType" = "Transparent"
            "Queue" = "Transparent+200"
            "RenderPipeline" = "UniversalPipeline"
        }
        // 普通半透明叠加（在黑暗遮罩之后渲染，所以不被黑暗乘掉）
        Blend SrcAlpha OneMinusSrcAlpha
        ZWrite Off
        Cull Off

        Pass
        {
            Name "EchoOutline"
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

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

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            CBUFFER_START(UnityPerMaterial)
                float4 _OutlineColor;
                float  _OutlineWidth;
                float4 _MainTex_ST;
            CBUFFER_END

            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);
            float4 _MainTex_TexelSize;

            // 全局回声参数（由 EchoSystem 通过 Shader.SetGlobalX 设置，不是材质属性）
            float4 _EchoCenter;            // xy = 回声中心世界坐标 XZ
            float  _EchoRadius;            // 当前波纹前沿半径
            float  _EchoWidth;             // 波纹前沿柔和宽度
            float  _EchoOutlineIntensity;  // 整体描边强度（维持=1, 消散=1→0, 关闭=0）
            float4 _EchoOutlineColor;      // 全局描边颜色（默认白色）

            // 8 方向采样偏移（对角线用 0.7071 保持等距）
            static const float2 _Offsets[8] = {
                float2( 1.0,  0.0), float2(-1.0,  0.0), float2( 0.0,  1.0), float2( 0.0, -1.0),
                float2( 0.7071,  0.7071), float2(-0.7071,  0.7071),
                float2( 0.7071, -0.7071), float2(-0.7071, -0.7071)
            };

            // 带边界检查的 alpha 采样：UV 超出 [0,1] 视为空（0），避免边缘错误填充
            float sampleAlphaBounded(float2 uv)
            {
                float a = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv).a;
                float inBounds = step(0.0, uv.x) * step(uv.x, 1.0)
                               * step(0.0, uv.y) * step(uv.y, 1.0);
                return a * inBounds;
            }

            Varyings vert(Attributes input)
            {
                Varyings output;
                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_TRANSFER_INSTANCE_ID(input, output);

                float3 posOS = input.positionOS.xyz;
                output.positionCS = TransformObjectToHClip(posOS);
                output.uv = TRANSFORM_TEX(input.uv, _MainTex);
                output.worldPos = TransformObjectToWorld(posOS);

                // 以精灵中心为基准向外扩展顶点，为外描边腾出屏幕空间
                float4 centerCS = TransformObjectToHClip(float3(0,0,0));
                float2 csDir = output.positionCS.xy - centerCS.xy;
                float len = length(csDir);
                csDir = (len > 0.0001) ? csDir / len : float2(1.0, 0.0);
                float2 ndcPerPixel = 2.0 / _ScreenParams.xy;
                output.positionCS.xy += csDir * ndcPerPixel * _OutlineWidth;

                return output;
            }

            half4 frag(Varyings input) : SV_Target
            {
                // ===== 回声距离检查 =====
                // 波纹前沿在 _EchoRadius；精灵距中心小于半径 → 已被扫描 → 显示描边
                float2 wp = input.worldPos.xz;
                float ed = distance(wp, _EchoCenter.xy);
                // smoothstep: 距离从 (radius-width) 到 radius 时从1降到0；取反 = 半径内为1
                float reached = 1.0 - smoothstep(_EchoRadius - _EchoWidth, _EchoRadius, ed);
                float intensity = reached * _EchoOutlineIntensity;
                // 回声未到达或已关闭 → 直接丢弃，零开销
                if (intensity < 0.001) discard;

                // ===== 膨胀采样得到描边 =====
                float2 uv = input.uv;
                float origAlpha = sampleAlphaBounded(uv);

                float2 texel = _MainTex_TexelSize.xy;
                float w = max(1.0, _OutlineWidth);
                float dilatedAlpha = 0.0;
                [unroll]
                for (int i = 0; i < 8; i++)
                {
                    float2 ouv = uv + _Offsets[i] * texel * w;
                    dilatedAlpha = max(dilatedAlpha, sampleAlphaBounded(ouv));
                }
                // 描边 = 邻居有 alpha 但中心没有（外描边）
                float outline = step(0.5, dilatedAlpha) * (1.0 - step(0.5, origAlpha));

                // ===== 输出 =====
                float3 col = _EchoOutlineColor.rgb * _OutlineColor.rgb;
                float alpha = outline * intensity * _EchoOutlineColor.a * _OutlineColor.a;
                return half4(col, alpha);
            }
            ENDHLSL
        }
    }
    FallBack Off
}
