1
This commit is contained in:
148
unity/Assets/Light/shaders/SpriteEchoOutline.shader
Normal file
148
unity/Assets/Light/shaders/SpriteEchoOutline.shader
Normal file
@@ -0,0 +1,148 @@
|
||||
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
|
||||
}
|
||||
9
unity/Assets/Light/shaders/SpriteEchoOutline.shader.meta
Normal file
9
unity/Assets/Light/shaders/SpriteEchoOutline.shader.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: Dn9OtiKqB35oskIeVe74RmLVIVNUFiTsesLMxl9tEuOicYXOA+RAQzY=
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user