shader_type canvas_item; uniform vec4 color_a : source_color = vec4(0.05, 0.06, 0.10, 1.0); // base uniform vec4 color_b : source_color = vec4(0.07, 0.10, 0.18, 1.0); // mid uniform vec4 color_c : source_color = vec4(0.10, 0.16, 0.28, 1.0); // accent // Swirl controls uniform float swirl_strength = 0.8; // base twist near center uniform float swirl_radius = 1.6; // falloff (bigger = more spread) uniform float swirl_speed = 0.2; // base animation speed // Secondary swirl "eddies" uniform float secondary_swirl_strength = 0.5; uniform float secondary_swirl_radius = 2.4; uniform vec2 secondary_swirl_center = vec2(0.2, 0.8); // in UV space // Extra flavor uniform float time_scale = 1.0; // global time multiplier uniform float vignette_strength = 0.6; // darken edges uniform float grain_amount = 0.006; // tiny grain to fight banding // Noise texture (fbm perlin) uniform sampler2D noise_tex; uniform float noise_uv_scale = 2.5; // tiling of noise uniform float noise_distort_amount = 0.035; // how much noise warps UVs uniform float swirl_noise_amount = 0.6; // how much noise affects swirl angle uniform float swirl_noise_scale = 1.8; // scale for angle noise sampling uniform float accent_noise_amount = 0.1; // how much noise modulates center accent vec2 map_uv_wrapping(vec2 uv, int mode) { switch (mode) { case 1: uv = clamp(uv, 0.0, 1.0); break; case 2: uv = fract(uv / 2.0); uv = min(uv, 1.0 - uv) * 2.0; break; case 3: uv = clamp(abs(uv), -1.0, 1.0); break; } return uv; } vec4 textureWrapped(sampler2D samp, vec2 uv, int mode) { uv = map_uv_wrapping(uv, mode); return textureLod(samp, uv, 0); //return texture(samp, uv); } void fragment() { float t = TIME * time_scale; // Keep a copy of original UV for some effects vec2 base_uv = UV; // === Noise-based UV distortion ======================================= vec2 noise_uv = base_uv * noise_uv_scale + vec2(t * 0.03, -t * 0.025); vec3 noise_sample = texture(noise_tex, noise_uv).rgb; // fbm: smooth 0-1 // Use red/green channels to gently distort UVs vec2 uv = base_uv + (noise_sample.rg - 0.5) * noise_distort_amount; // === Swirl fields ===================================================== // Main swirl around screen center vec2 center = vec2(0.5, 0.5); vec2 pos = uv - center; float r = length(pos); // distance from center float angle = atan(pos.y, pos.x); // original polar angle // Primary swirl: fades with radius and pulses over time float base_swirl = swirl_strength * exp(-r * swirl_radius) * sin(t * swirl_speed + r * 6.28318); // Secondary swirl "eddy" region somewhere else on the screen vec2 pos2 = uv - secondary_swirl_center; float r2 = length(pos2); float secondary_swirl = secondary_swirl_strength * exp(-r2 * secondary_swirl_radius) * sin(t * swirl_speed * 0.7 + r2 * 6.28318 * 0.5); // Noise-driven swirl angle perturbation (local turbulence) vec2 swirl_noise_uv = base_uv * swirl_noise_scale + vec2(-t * 0.02, t * 0.015); float swirl_noise = (textureWrapped(noise_tex, swirl_noise_uv, 2).r) * swirl_noise_amount; angle += base_swirl + secondary_swirl + swirl_noise; vec2 swirled = center + vec2(cos(angle), sin(angle)) * r; // === Color gradient & accent ========================================= // Vertical gradient (using swirled Y) float g = clamp(swirled.y, 0.0, 1.0); vec4 col = mix(color_a, color_b, g); // Accent towards center, modulated slightly by noise float accent_factor = smoothstep(0.2, 0.8, 1.0 - r); float accent_noise = (noise_sample.b - 0.5) * accent_noise_amount; accent_factor = clamp(accent_factor + accent_noise, 0.0, 1.0); col = mix(col, color_c, accent_factor * 0.6); // === Vignette ========================================================= float vignette = smoothstep(0, 1.0, r); float vignette_mul = mix(1.0, 1.0 - vignette_strength, vignette); col.rgb *= vignette_mul; // === High-frequency grain to kill banding ============================ // Use analytic noise for real high-frequency variation float grain = fract( sin(dot(base_uv * 128.0 + vec2(t * 0.5, -t * 0.35), vec2(12.9898, 78.233))) * 43758.5453 ); col.rgb += (grain - 0.5) * grain_amount; COLOR = col; }