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 } } }