Files
gold_dolphin/unity/Assets/Light/shaders/SpriteEchoOutline.shader
2026-06-27 03:37:32 +08:00

149 lines
6.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}