From add1cf3cc0beb624c7570fe62f153c7c8d57e843 Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Sun, 1 Feb 2026 10:50:14 -0600 Subject: [PATCH] feat(immersive): add GPU blur + noise distortion in shader - Multi-sample blur: 12 samples in circular pattern - Noise-based UV distortion to break up pixelation - Random sample offsets for organic blur effect - All blur computation is GPU-side (no CPU cost) --- src/lib/immersive/shaders/ambient.frag | 92 +++++++++++++++++++------- 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/src/lib/immersive/shaders/ambient.frag b/src/lib/immersive/shaders/ambient.frag index 5d25bd436..fc7b0c2d5 100644 --- a/src/lib/immersive/shaders/ambient.frag +++ b/src/lib/immersive/shaders/ambient.frag @@ -2,59 +2,101 @@ /** * Ambient Motion Fragment Shader * - * Adds subtle GPU-driven motion to the background: + * Adds GPU-driven motion and blur simulation to the background: + * - Multi-sample blur (samples multiple nearby points) * - UV drift (slow wandering) * - Zoom oscillation (breathing effect) * - Color breathing (subtle brightness pulsing) - * - * All effects are controlled by u_intensity (0 = static, 1 = full motion). - * All computation is GPU-side - no CPU per-frame work. + * - Noise-based distortion to break up pixelation */ precision mediump float; -// Input from vertex shader in vec2 v_texCoord; - -// Output out vec4 fragColor; -// Uniforms uniform sampler2D u_texture; -uniform float u_time; // Time in seconds -uniform float u_intensity; // Motion intensity (0.0 - 1.0) +uniform float u_time; +uniform float u_intensity; -// Constants for motion parameters - made MORE visible -const float DRIFT_SPEED_X = 0.15; -const float DRIFT_SPEED_Y = 0.12; -const float DRIFT_AMOUNT = 0.08; // 8% UV drift +// Motion parameters +const float DRIFT_SPEED_X = 0.12; +const float DRIFT_SPEED_Y = 0.09; +const float DRIFT_AMOUNT = 0.06; -const float ZOOM_SPEED = 0.1; -const float ZOOM_AMOUNT = 0.05; // 5% zoom oscillation +const float ZOOM_SPEED = 0.08; +const float ZOOM_AMOUNT = 0.04; -const float BREATH_SPEED = 0.2; -const float BREATH_AMOUNT = 0.12; // 12% brightness pulse +const float BREATH_SPEED = 0.15; +const float BREATH_AMOUNT = 0.08; + +// Blur simulation parameters +const float BLUR_RADIUS = 0.015; // UV space blur radius +const int BLUR_SAMPLES = 12; // Number of samples for blur + +// Simple pseudo-random function +float random(vec2 st) { + return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123); +} + +// Smooth noise function +float noise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + + float a = random(i); + float b = random(i + vec2(1.0, 0.0)); + float c = random(i + vec2(0.0, 1.0)); + float d = random(i + vec2(1.0, 1.0)); + + vec2 u = f * f * (3.0 - 2.0 * f); + + return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; +} void main() { vec2 uv = v_texCoord; vec2 center = vec2(0.5); - // UV drift - slow wandering motion + // UV drift float driftX = sin(u_time * DRIFT_SPEED_X) * DRIFT_AMOUNT * u_intensity; float driftY = cos(u_time * DRIFT_SPEED_Y * 1.3) * DRIFT_AMOUNT * u_intensity; uv += vec2(driftX, driftY); - // Zoom oscillation - subtle breathing + // Zoom oscillation float zoom = 1.0 + sin(u_time * ZOOM_SPEED) * ZOOM_AMOUNT * u_intensity; uv = center + (uv - center) / zoom; - // Clamp UV to avoid edge artifacts - uv = clamp(uv, 0.0, 1.0); + // Add subtle noise-based distortion to break up pixelation + float noiseScale = 8.0; + float distortAmount = 0.008; + float n1 = noise(uv * noiseScale + u_time * 0.1); + float n2 = noise(uv * noiseScale + 100.0 + u_time * 0.1); + uv += vec2(n1 - 0.5, n2 - 0.5) * distortAmount; - // Sample texture - vec4 color = texture(u_texture, uv); + // Multi-sample blur to smooth out the texture + vec4 color = vec4(0.0); + float totalWeight = 0.0; - // Color breathing - subtle brightness pulsing + for (int i = 0; i < BLUR_SAMPLES; i++) { + float angle = float(i) * 6.28318530718 / float(BLUR_SAMPLES); + float r = BLUR_RADIUS * (0.5 + 0.5 * random(uv + float(i))); + vec2 offset = vec2(cos(angle), sin(angle)) * r; + + vec2 sampleUV = clamp(uv + offset, 0.0, 1.0); + float weight = 1.0 - length(offset) / BLUR_RADIUS; + + color += texture(u_texture, sampleUV) * weight; + totalWeight += weight; + } + + // Add center sample with higher weight + color += texture(u_texture, clamp(uv, 0.0, 1.0)) * 2.0; + totalWeight += 2.0; + + color /= totalWeight; + + // Color breathing float breath = 1.0 + sin(u_time * BREATH_SPEED) * BREATH_AMOUNT * u_intensity; color.rgb *= breath;