#ifdef GL_ES precision mediump float; #endif // Canvas uniforms uniform vec2 u_resolution; uniform float u_uvCorr; uniform vec2 u_mouse; uniform vec2 u_onePixel; // Gradient uniforms uniform float u_aspect; uniform vec2 u_ellipseCenter, u_majorVertex; uniform vec2 u_focalPoint; uniform float u_mouseStrength, u_ellipseScale; uniform vec3 SW0, SW1, SW2, SW3; uniform float stop0, stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8; uniform float u_scrollProgress; // Precomputed gradient uniforms uniform vec2 u_majorDir; uniform vec2 u_minorDir; uniform float u_majorRadius; uniform float u_minorRadius; uniform vec2 u_invRadii; uniform vec2 u_renderCenter; // Center for rendering (may differ from u_ellipseCenter) // Distortion uniforms uniform float u_distortion; // Max pixel offset in px (0.0 = no distortion) uniform float u_sections; // Number of vertical sections uniform float u_split; // Ratio of reversed gradient in each section // Precomputed distortion uniforms uniform float u_invBig; // 1.0 / (1.0 - u_split) uniform float u_invSplit; // 1.0 / u_split // Gradient color function vec3 getColor(float t) { float f; if (t < stop1) { return SW0; } else if (t < stop2) { f = clamp((t - stop1) / (stop2 - stop1), 0.0, 1.0); return mix(SW0, SW1, f); } else if (t < stop3) { f = clamp((t - stop2) / (stop3 - stop2), 0.0, 1.0); return mix(SW1, SW2, f); } else if (t < stop4) { f = clamp((t - stop3) / (stop4 - stop3), 0.0, 1.0); return mix(SW2, SW3, f); } else if (t < stop5) { f = clamp((t - stop4) / (stop5 - stop4), 0.0, 1.0); return mix(SW3, SW2, f); } else if (t < stop6) { f = clamp((t - stop5) / (stop6 - stop5), 0.0, 1.0); return mix(SW2, SW1, f); } else if (t < stop7) { f = clamp((t - stop6) / (stop7 - stop6), 0.0, 1.0); return mix(SW1, SW0, f); } else { return SW0; } } // Displacement map function float dispMap(vec2 uv) { float local = fract(uv.x * max(u_sections, 1.0)); float big = 1.0 - u_split; float phase = step(big, local); // White to black over [0...big] float val1 = 1.0 - local * u_invBig; // Black to white over [big...1] float val2 = (local - big) * u_invSplit; return mix(val1, val2, phase); } // Calculate gradient at given UV coordinates vec3 calculateGradient(vec2 uv) { // Apply aspect ratio correction vec2 renderCenter = u_renderCenter; uv.y = (uv.y - renderCenter.y) * u_uvCorr + renderCenter.y; // Compute focal point with mouse influence vec2 mouseUV = u_mouse / u_resolution; vec2 focal = mix(u_focalPoint, mouseUV, u_mouseStrength); // Check if focal is too close to center (use ellipse center for calculations) vec2 focalDelta = focal - u_ellipseCenter; if (dot(focalDelta, focalDelta) < 1e-12) { focal += u_onePixel; } // Transform to ellipse space using render center for position vec2 uvDelta = uv - renderCenter; vec2 P = vec2( dot(uvDelta, u_majorDir) * u_invRadii.x, dot(uvDelta, u_minorDir) * u_invRadii.y ); // Focal point in ellipse space (still relative to calculation center) vec2 F = vec2( dot(focalDelta, u_majorDir) * u_invRadii.x, dot(focalDelta, u_minorDir) * u_invRadii.y ); // Clamp focal to ellipse boundary if needed float focalLenSq = dot(F, F); if (focalLenSq > 1.0) { F *= inversesqrt(focalLenSq); } // Ray-ellipse intersection vec2 D = P - F; float L = length(D); // Early exit if point is at focal if (L < 1e-6) { return SW0; } vec2 d = D / L; float b = dot(F, d); float c = focalLenSq - 1.0; float disc = b*b - c; // Compute intersection float T = (disc > 0.0) ? (-b + sqrt(disc)) : 1.0; float t = clamp(L / T, 0.0, 1.0); return getColor(t); } void main() { vec2 uv = gl_FragCoord.xy / u_resolution.xy; // Calculate displacement float dNorm = dispMap(uv) - 0.5; float offsetU = dNorm * u_distortion / u_resolution.x; // Apply displacement vec2 displaced = uv + vec2(offsetU, 0.0); displaced = clamp(displaced, 0.0, 1.0); // Calculate gradient at displaced position vec3 color = calculateGradient(displaced); gl_FragColor = vec4(color, 1.0); }