Files
ichni_Official/Assets/Shaders/UI/UI_ContourFlow.shader
SoulliesOfficial a34461d31f 阶段性完成
2026-07-25 13:27:53 -04:00

307 lines
11 KiB
Plaintext

Shader "Ichni/UI/Contour Flow"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1, 1, 1, 1)
[Header(Organic Motion)]
_FlowScale ("Flow Scale", Range(0.5, 12)) = 4.0
_FlowSpeed ("Flow Speed", Range(-2, 2)) = 0.18
_FlowStrength ("Flow Strength", Range(0, 0.03)) = 0.004
_RippleCenter ("Ripple Center", Vector) = (0.38, 0.48, 0, 0)
_RippleRadius ("Ripple Radius", Range(0.05, 1.5)) = 0.9
_RippleFrequency ("Ripple Frequency", Range(1, 80)) = 32
_RippleSpeed ("Ripple Speed", Range(-10, 10)) = 0.7
_RippleStrength ("Ripple Strength", Range(0, 0.03)) = 0.003
[Header(Contour Energy)]
_LineColor ("Line Color", Color) = (0.36, 0.58, 1, 1)
_AccentColor ("Pulse Accent", Color) = (0.64, 0.38, 1, 1)
_LineThreshold ("Line Threshold", Range(0, 1)) = 0.17
_LineSoftness ("Line Softness", Range(0.001, 0.5)) = 0.14
_LineBoost ("Line Boost", Range(0, 2)) = 0.38
_PulseSpeed ("Pulse Speed", Range(-2, 2)) = 0.08
_PulseWidth ("Pulse Width", Range(0.02, 0.48)) = 0.14
_PulseIntensity ("Pulse Intensity", Range(0, 3)) = 0.9
_BreathSpeed ("Breath Speed", Range(0, 4)) = 0.55
_EchoDistance ("Colored Echo Distance", Range(0, 0.02)) = 0.002
_EchoStrength ("Colored Echo Strength", Range(0, 2)) = 0.22
[Header(Bottom Energy)]
_BottomGlowColor ("Bottom Glow Color", Color) = (0.04, 0.2, 1, 1)
_BottomGlowStrength ("Bottom Glow Strength", Range(0, 1)) = 0.14
_BottomGlowPower ("Bottom Glow Falloff", Range(0.5, 8)) = 3.2
[Header(UI)]
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_CLIP_RECT)] _UseClipRect ("Use UI Clip Rect", Float) = 0
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use UI Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "False"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Contour Flow UI"
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
struct appdata_t
{
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float4 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
half4 mask : TEXCOORD2;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float _UIMaskSoftnessX;
float _UIMaskSoftnessY;
float _FlowScale;
float _FlowSpeed;
float _FlowStrength;
float4 _RippleCenter;
float _RippleRadius;
float _RippleFrequency;
float _RippleSpeed;
float _RippleStrength;
fixed4 _LineColor;
fixed4 _AccentColor;
float _LineThreshold;
float _LineSoftness;
float _LineBoost;
float _PulseSpeed;
float _PulseWidth;
float _PulseIntensity;
float _BreathSpeed;
float _EchoDistance;
float _EchoStrength;
fixed4 _BottomGlowColor;
float _BottomGlowStrength;
float _BottomGlowPower;
v2f vert(appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.worldPosition = v.vertex;
o.vertex = UnityObjectToClipPos(o.worldPosition);
o.texcoord.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.texcoord.zw = v.texcoord;
o.color = v.color * _Color;
float2 pixelSize = o.vertex.w;
pixelSize /= abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
o.mask = half4(
v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw,
0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy))
);
return o;
}
half PerceivedLuminance(half3 color)
{
#ifndef UNITY_COLORSPACE_GAMMA
color = LinearToGammaSpace(color);
#endif
return dot(color, half3(0.2126, 0.7152, 0.0722));
}
float2 BuildAnimatedUV(float2 uv, out float2 flowDirection, out float flowSignal)
{
float time = _Time.y;
float aspect = max(_MainTex_TexelSize.z / max(_MainTex_TexelSize.w, 1.0), 0.001);
float2 aspectPosition = uv - 0.5;
aspectPosition.x *= aspect;
float flowTime = time * _FlowSpeed;
float flowX =
sin(aspectPosition.y * _FlowScale + flowTime) +
0.45 * sin((aspectPosition.x + aspectPosition.y) * (_FlowScale * 0.73) - flowTime * 0.61);
float flowY =
cos(aspectPosition.x * (_FlowScale * 0.89) - flowTime * 0.83) +
0.45 * sin((aspectPosition.x - aspectPosition.y) * (_FlowScale * 0.57) + flowTime * 0.47);
float2 flow = float2(flowX, flowY) / 1.45;
float2 flowUV = flow;
flowUV.x /= aspect;
float2 rippleDelta = uv - _RippleCenter.xy;
rippleDelta.x *= aspect;
float rippleDistance = max(length(rippleDelta), 0.0001);
float2 rippleDirection = rippleDelta / rippleDistance;
float rippleEnvelope = 1.0 - smoothstep(_RippleRadius * 0.2, _RippleRadius, rippleDistance);
float rippleWave = sin(rippleDistance * _RippleFrequency - time * _RippleSpeed);
float2 rippleUV = rippleDirection;
rippleUV.x /= aspect;
float2 animatedUV =
uv +
flowUV * _FlowStrength +
rippleUV * (rippleWave * rippleEnvelope * _RippleStrength);
flowDirection = normalize(flowUV + rippleUV * (rippleWave * rippleEnvelope) + float2(0.001, 0.001));
flowSignal = (flowX + flowY + rippleWave * rippleEnvelope) * 0.3333;
float2 halfTexel = _MainTex_TexelSize.xy * 0.5;
return clamp(animatedUV, halfTexel, 1.0 - halfTexel);
}
fixed4 frag(v2f i) : SV_Target
{
float2 flowDirection;
float flowSignal;
float2 animatedUV = BuildAnimatedUV(i.texcoord.xy, flowDirection, flowSignal);
half4 source = tex2D(_MainTex, animatedUV) + _TextureSampleAdd;
half sourceLuminance = PerceivedLuminance(source.rgb);
half echoDifference = 0;
UNITY_BRANCH
if (_EchoStrength > 0.0001 && _EchoDistance > 0.000001)
{
float2 halfTexel = _MainTex_TexelSize.xy * 0.5;
float2 echoUV = clamp(
animatedUV - flowDirection * _EchoDistance,
halfTexel,
1.0 - halfTexel
);
half echoLuminance = PerceivedLuminance(tex2D(_MainTex, echoUV).rgb);
echoDifference = saturate(abs(echoLuminance - sourceLuminance) * 5.0);
}
half lineMask = smoothstep(
_LineThreshold,
_LineThreshold + max(_LineSoftness, 0.001),
sourceLuminance
);
float pulsePhase = frac(
animatedUV.y * 0.78 +
animatedUV.x * 0.22 +
flowSignal * 0.035 -
_Time.y * _PulseSpeed
);
float pulseDistance = abs(pulsePhase - 0.5);
float pulseFeather = max(fwidth(pulsePhase) * 1.5, 0.006);
half pulse = 1.0 - smoothstep(
_PulseWidth,
_PulseWidth + pulseFeather,
pulseDistance
);
half breath = 0.8 + 0.2 * sin(
_Time.y * _BreathSpeed +
sourceLuminance * 12.0 +
flowSignal
);
half lineEnergy =
lineMask *
breath *
(0.35 + pulse * _PulseIntensity) *
_LineBoost;
half3 movingLineColor = lerp(
_LineColor.rgb * _LineColor.a,
_AccentColor.rgb * _AccentColor.a,
pulse
);
half echoEnergy = echoDifference * (0.35 + pulse * 0.65) * _EchoStrength;
half bottomGlow = pow(saturate(1.0 - i.texcoord.w), _BottomGlowPower);
bottomGlow *= _BottomGlowStrength *
(0.9 + 0.1 * cos(_Time.y * _BreathSpeed * 0.6));
half3 enhancedColor =
source.rgb +
movingLineColor * lineEnergy +
_AccentColor.rgb * (_AccentColor.a * echoEnergy) +
_BottomGlowColor.rgb * (_BottomGlowColor.a * bottomGlow);
fixed4 color;
color.rgb = enhancedColor * i.color.rgb;
color.a = source.a * i.color.a;
#ifdef UNITY_UI_CLIP_RECT
half2 clipMask = saturate(
(_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw
);
color.a *= clipMask.x * clipMask.y;
#endif
#ifdef UNITY_UI_ALPHACLIP
clip(color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}