Abyssal Caustics
A high-performance WebGPU caustics simulation layering multiple Voronoi noise fields.
60 FPS • WebGPU
WGSL Shader Source
struct Uniforms { resolution: vec2f, time: f32, padding: f32, mouse: vec2f, speed: f32, scale: f32, }
@group(0) @binding(0) var<uniform> u: Uniforms;
@vertex
fn vs_main(@builtin(vertex_index) vertexIndex: 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[vertexIndex], 0.0, 1.0);
}
fn hash22(p: vec2f) -> vec2f {
var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));
p3 = p3 + dot(p3, p3.yzx + 33.33);
return fract((p3.xx + p3.yz) * p3.zy);
}
fn voronoi(x: vec2f, time: f32) -> vec2f {
let n = floor(x);
let f = fract(x);
var m = vec2f(8.0);
for(var j = -1; j <= 1; j++) {
for(var i = -1; i <= 1; i++) {
let g = vec2f(f32(i), f32(j));
let o = hash22(n + g);
let pos = 0.5 + 0.5 * sin(time + 6.2831 * o);
let d = length(g - f + pos);
if(d < m.x) { m.x = d; m.y = o.x; }
}
}
return m;
}
@fragment
fn fs_main(@builtin(position) fragCoord: vec4f) -> @location(0) vec4f {
let uv = fragCoord.xy / u.resolution;
let t = u.time * u.speed * 0.5;
let mouseOffset = (u.mouse - 0.5) * 2.0;
var p = (uv - 0.5) * u.scale + mouseOffset;
p.x *= u.resolution.x / u.resolution.y;
var v1 = voronoi(p * 2.0 + t, t);
var v2 = voronoi(p * 4.0 - t * 1.5, t * 1.2);
var v3 = voronoi(p * 8.0 + vec2f(sin(t), cos(t)), t * 0.8);
let caustics = pow(1.0 - v1.x, 3.0) + pow(1.0 - v2.x, 2.0) * 0.5 + pow(1.0 - v3.x, 2.0) * 0.25;
let baseColor = vec3f(0.05, 0.1, 0.2);
let lightColor = vec3f(0.3, 0.7, 1.0) * caustics;
let finalColor = baseColor + lightColor;
return vec4f(finalColor, 1.0);
} Properties
#webgpu#caustics#metalforge#fluid
Author: Math Art Core Target: 60 FPS