Files
aibis-dream/Assets/Render/CRTOverlayEffect.shader
2026-02-09 14:02:41 +08:00

273 lines
9.4 KiB
GLSL

Shader "UI/CRTOverlayEffect"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
[Header(Scanlines)]
_ScanlineIntensity ("Scanline Intensity", Range(0, 1)) = 0.3
_ScanlineCount ("Scanline Count", Range(50, 800)) = 300
_ScanlineSpeed ("Scanline Speed", Range(0, 5)) = 0.5
[Header(Noise)]
_NoiseIntensity ("Noise Intensity", Range(0, 1)) = 0.15
_NoiseSpeed ("Noise Speed", Range(0, 50)) = 15
_NoiseScale ("Noise Scale", Range(1, 100)) = 50
[Header(Chromatic Aberration)]
_ChromaticAberration ("Chromatic Aberration", Range(0, 0.02)) = 0.003
[Header(Vignette)]
_VignetteIntensity ("Vignette Intensity", Range(0, 1)) = 0.3
_VignetteSmoothness ("Vignette Smoothness", Range(0.01, 1)) = 0.4
[Header(Screen Curvature)]
_CurvatureAmount ("Curvature Amount", Range(0, 0.1)) = 0.02
[Header(Flicker)]
_FlickerIntensity ("Flicker Intensity", Range(0, 0.1)) = 0.02
_FlickerSpeed ("Flicker Speed", Range(0, 30)) = 8
[Header(Glow)]
_GlowIntensity ("Glow Intensity", Range(0, 1)) = 0.1
_GlowColor ("Glow Color", Color) = (0.2, 0.8, 1, 1)
// 用于控制 stencil 等 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
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
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 "CRTOverlay"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float4 _ClipRect;
// Effect parameters
float _ScanlineIntensity;
float _ScanlineCount;
float _ScanlineSpeed;
float _NoiseIntensity;
float _NoiseSpeed;
float _NoiseScale;
float _ChromaticAberration;
float _VignetteIntensity;
float _VignetteSmoothness;
float _CurvatureAmount;
float _FlickerIntensity;
float _FlickerSpeed;
float _GlowIntensity;
fixed4 _GlowColor;
// 简单的噪点函数
float random(float2 st)
{
return frac(sin(dot(st.xy, float2(12.9898, 78.233))) * 43758.5453123);
}
// 2D噪点
float noise(float2 st)
{
float2 i = floor(st);
float2 f = frac(st);
float a = random(i);
float b = random(i + float2(1.0, 0.0));
float c = random(i + float2(0.0, 1.0));
float d = random(i + float2(1.0, 1.0));
float2 u = f * f * (3.0 - 2.0 * f);
return lerp(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.worldPosition = v.vertex;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.color = v.color * _Color;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
float2 uv = i.uv;
float time = _Time.y;
// 1. 屏幕弯曲效果
float2 curvedUV = uv;
if (_CurvatureAmount > 0)
{
float2 centered = uv - 0.5;
float dist = dot(centered, centered);
curvedUV = uv + centered * dist * _CurvatureAmount;
}
// 检查是否超出边界
if (curvedUV.x < 0 || curvedUV.x > 1 || curvedUV.y < 0 || curvedUV.y > 1)
{
return fixed4(0, 0, 0, 0);
}
// 2. 采样基础颜色 (带色差)
fixed4 col = fixed4(0, 0, 0, 0);
if (_ChromaticAberration > 0)
{
float2 dir = normalize(curvedUV - 0.5);
float dist = length(curvedUV - 0.5);
float aberration = _ChromaticAberration * dist;
col.r = tex2D(_MainTex, curvedUV + dir * aberration).r;
col.g = tex2D(_MainTex, curvedUV).g;
col.b = tex2D(_MainTex, curvedUV - dir * aberration).b;
col.a = tex2D(_MainTex, curvedUV).a;
}
else
{
col = tex2D(_MainTex, curvedUV);
}
col *= i.color;
// 3. 扫描线
if (_ScanlineIntensity > 0)
{
float scanline = sin((curvedUV.y + time * _ScanlineSpeed * 0.1) * _ScanlineCount * 3.14159) * 0.5 + 0.5;
scanline = pow(scanline, 1.5);
col.rgb *= 1.0 - (_ScanlineIntensity * (1.0 - scanline));
// 添加水平扫描线移动效果
float movingScanline = sin((curvedUV.y - time * 0.2) * 20.0) * 0.5 + 0.5;
movingScanline = step(0.99, movingScanline);
col.rgb += movingScanline * 0.03;
}
// 4. 噪点
if (_NoiseIntensity > 0)
{
float2 noiseUV = curvedUV * _NoiseScale;
float n = noise(noiseUV + time * _NoiseSpeed);
n = n * 2.0 - 1.0; // 转换到 -1 到 1 范围
col.rgb += n * _NoiseIntensity;
// 添加一些随机的小噪点/颗粒
float grain = random(curvedUV * 1000.0 + time * 100.0);
grain = (grain - 0.5) * _NoiseIntensity * 0.5;
col.rgb += grain;
}
// 5. 闪烁
if (_FlickerIntensity > 0)
{
float flicker = 1.0 + sin(time * _FlickerSpeed) * _FlickerIntensity;
flicker *= 1.0 + random(float2(time * 0.1, 0)) * _FlickerIntensity * 0.5;
col.rgb *= flicker;
}
// 6. 暗角
if (_VignetteIntensity > 0)
{
float2 vignetteUV = curvedUV * (1.0 - curvedUV);
float vignette = vignetteUV.x * vignetteUV.y * 15.0;
vignette = pow(vignette, _VignetteSmoothness);
vignette = saturate(vignette);
col.rgb *= lerp(1.0 - _VignetteIntensity, 1.0, vignette);
}
// 7. 发光效果
if (_GlowIntensity > 0)
{
col.rgb += _GlowColor.rgb * _GlowIntensity * col.a;
}
// 8. 添加一些RGB分离的条纹效果 (模拟CRT的RGB像素)
float rgbStripe = frac(curvedUV.x * 300.0);
float3 rgbMask = float3(
step(rgbStripe, 0.33),
step(0.33, rgbStripe) * step(rgbStripe, 0.66),
step(0.66, rgbStripe)
);
// 轻微的RGB条纹效果
col.rgb = lerp(col.rgb, col.rgb * (0.8 + rgbMask * 0.4), 0.05);
// 应用 UI 裁剪
col.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect);
#ifdef UNITY_UI_ALPHACLIP
clip(col.a - 0.001);
#endif
return col;
}
ENDCG
}
}
}