97

Liquid Glass Orbs

Smooth-min merged spheres with simulated environment mapping for a glassy, refractive look.

60 FPS WebGPU

WGSL Shader Source

struct Uniforms { resolution: vec2f, time: f32, padding: f32, mouse: vec2f, blend: f32, }
@group(0) @binding(0) var<uniform> u: Uniforms;

@vertex
fn vs_main(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
  let pos = array(vec2f(-1.0,-1.0), vec2f(3.0,-1.0), vec2f(-1.0,3.0));
  return vec4f(pos[vi], 0.0, 1.0);
}

fn smin(a: f32, b: f32, k: f32) -> f32 {
  let h = clamp(0.5 + 0.5*(b-a)/k, 0.0, 1.0);
  return mix(b, a, h) - k*h*(1.0-h);
}

fn map(p: vec3f) -> f32 {
  let t = u.time * 0.5;
  let p1 = vec3f(sin(t*1.1)*1.5, cos(t*1.3)*1.5, sin(t*1.5)*1.5);
  let p2 = vec3f(cos(t*1.2)*1.5, sin(t*1.4)*1.5, cos(t*1.6)*1.5);
  let d1 = length(p - p1) - 1.0;
  let d2 = length(p - p2) - 1.0;
  let d3 = length(p) - 1.2;
  return smin(smin(d1, d2, u.blend), d3, u.blend);
}

fn getNormal(p: vec3f) -> vec3f {
  let e = vec2f(0.01, 0.0);
  return normalize(vec3f(
    map(p+e.xyy) - map(p-e.xyy),
    map(p+e.yxy) - map(p-e.yxy),
    map(p+e.yyx) - map(p-e.yyx)
  ));
}

@fragment
fn fs_main(@builtin(position) fragCoord: vec4f) -> @location(0) vec4f {
  let uv = (fragCoord.xy - 0.5*u.resolution) / u.resolution.y;
  var ro = vec3f(0.0, 0.0, -5.0);
  var rd = normalize(vec3f(uv, 1.0));
  
  let mx = (u.mouse.x - 0.5) * 6.28;
  let my = (u.mouse.y - 0.5) * 6.28;
  
  // simple camera rot
  let cx = cos(mx); let sx = sin(mx);
  let cy = cos(my); let sy = sin(my);
  rd = vec3f(rd.x*cx - rd.z*sx, rd.y, rd.x*sx + rd.z*cx);
  ro = vec3f(ro.x*cx - ro.z*sx, ro.y, ro.x*sx + ro.z*cx);
  
  var t = 0.0;
  var i = 0;
  for(i=0; i<80; i++) {
    let p = ro + rd * t;
    let d = map(p);
    if(d < 0.001 || t > 20.0) { break; }
    t += d;
  }
  
  var col = vec3f(0.0);
  if(t < 20.0) {
    let p = ro + rd * t;
    let n = getNormal(p);
    let r = reflect(rd, n);
    // fake environment map
    let env = vec3f(0.5) + 0.5 * cos(r.xyx * 5.0 + u.time);
    let fresnel = pow(1.0 - max(dot(n, -rd), 0.0), 3.0);
    col = mix(vec3f(0.1, 0.3, 0.5), env, fresnel * 0.8 + 0.2);
  }
  
  return vec4f(col, 1.0);
}

Properties

#webgpu#raymarching#glass#smin
Author: Math Art Core Target: 60 FPS
ESC
↑↓ Navigate Select
101 Mathematical Artworks

Support the Project

Keep mathematical creative coding alive & open source

Or via Direct Payoneer ($0 Fee)
Payoneer Customer ID: $0 Fee Direct
24076084

💡 Payoneer app: Go to Pay → Pay to recipient → Enter ID 24076084.

Thank you for supporting generative mathematical animations! ✨