cuneus 0.5.0

A WGPU-based shader development tool
Documentation
// MIT License Enes Altun, 2025
// Trap technique: https://iquilezles.org/articles/ftrapsgeometric/
struct TimeUniform {
    time: f32,
    delta: f32,
    frame: u32,
    _padding: u32,
};
const PI = 3.14159;
@group(0) @binding(0) var<uniform> u_time: TimeUniform;
@group(1) @binding(0) var output: texture_storage_2d<rgba16float, write>;
@group(1) @binding(1) var<uniform> params: Params;

struct MouseUniform {
    position: vec2<f32>,         
    click_position: vec2<f32>,   
    wheel: vec2<f32>,            
    buttons: vec2<u32>,          
};
@group(2) @binding(0) var<uniform> u_mouse: MouseUniform;

struct Params {
    base_color: vec3<f32>,
    x: f32,
    rim_color: vec3<f32>,
    y: f32,
    accent_color: vec3<f32>,
    gamma_correction: f32,
    travel_speed: f32,
    iteration: i32,
    col_ext: f32,
    zoom: f32,
    trap_pow: f32,
    trap_x: f32,
    trap_y: f32,
    trap_c1: f32,
    aa: i32,
    trap_s1: f32,
    wave_speed: f32,
    fold_intensity: f32,
};
// nt: normalize trap
fn nt(d: f32, s: f32) -> f32 {
    return .5 * (1. + tanh(s * d));
}

// op: oscillate with pause
fn op(mn: f32, mx: f32, i: f32, p: f32, t: f32) -> f32 {
    let c = 2. * i + p;
    let m = t % c;
    if(m < i) {
        return mix(mx, mn, .5 - .5 * cos(PI * m / i));
    } else if(m < i + p) {
        return mn;
    } else {
        return mix(mn, mx, .5 - .5 * cos(PI * (m - i - p) / i));
    }
}
// im: implicit fractal calculation
fn im(c: vec2<f32>, t1: vec2<f32>, t2: vec2<f32>, t: f32) -> vec4<f32> {
    var z = vec2(0.);      // position
    var dz = vec2(1., 0.); // derivative
    var t1m = 1e20;        // trap1 min
    var t2m = 1e20;        // trap2 min
    var t1s = 0.;          // trap1 sum
    var t1c = 0.;          // trap1 count
    let mi = params.iteration;
    let dt = t * .001;
    var i = 0;
    for(; i < mi; i++) {
        dz = 2. * vec2(z.x * dz.x - z.y * dz.y, z.x * dz.y + z.y * dz.x) + vec2(1., 0.);
        let x = z.x * z.x - z.y * z.y + c.x;
        z.y = 2. * z.x * z.y + c.y;
        z.x = x;
        z += .1 * vec2(sin(.001 * dt), cos(.001 * dt));
        let d1 = length(z - t1);
        let f = 1. - smoothstep(.6, 1.4, d1);
        t1c += f;
        t1s += f * d1;
        t1m = min(t1m, d1);
        t2m = min(t2m, dot(z - t2, z - t2));
        if(dot(z, z) > 12.5) { break; }
    }
    let d = sqrt(dot(z, z) / dot(dz, dz)) * log(dot(z, z));
    return vec4(f32(i), d, t1s / max(t1c, 1.), t2m);
}
// g: gamma correction
fn g(c: vec3<f32>, g: f32) -> vec3<f32> {
    return pow(c, vec3(1. / g));
}
@compute @workgroup_size(16, 16, 1)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
    let ss = vec2<f32>(textureDimensions(output));
    let coords = vec2<u32>(global_id.xy);
    
    if (coords.x >= u32(ss.x) || coords.y >= u32(ss.y)) {
        return;
    }
    
    let frag = vec2(f32(coords.x), ss.y - f32(coords.y));
    let AA = params.aa;
    let t = u_time.time;
    let t01 = t * .4;
    // cp: camera path
    let cp = vec2(sin(.0002 * t * params.travel_speed / 10.), cos(.0002 * t * params.travel_speed / 10.));
    // p: pan position
    var p = vec2(.8085, .2607);
    if(t > 17.) { p.y += .00001 * (t - 1.); }
    // zl: zoom level
    let zl = op(.0005, .0005, 10., 5., t01);
    // t1, t2: traps
    let t1 = vec2(0., params.fold_intensity);
    let t2 = vec2(params.trap_x, params.trap_y) + params.wave_speed * vec2(cos(.3 * t), sin(.3 * t));
    var col = vec3(0.);
    for(var m = 0; m < AA; m++) {
        for(var n = 0; n < AA; n++) {
            let so = vec2(f32(m), f32(n)) / f32(AA);
            let mr = min(ss.x, ss.y);
            let uv = ((frag + so - .5 * ss) / mr * params.zoom + p + cp) * 2.033 - vec2(params.x, params.y);
            let zd = im(uv, t1, t2, t01);
            let ir = zd.x / f32(params.iteration);
            if(ir < 1.1) {
                let c1 = pow(clamp(2. * zd.y / zl, 0., 1.), .5);
                let c2 = pow(clamp(1.5 * zd.z, 0., 1.), 2.);
                let c3 = pow(clamp(.4 * zd.w, 0., 1.), .25);
                // cl1, cl2: colors based on params
                let cl1 = .5 + .5 * sin(vec3(3.) + 4. * c2 + params.rim_color);
                let cl2 = .5 + .5 * sin(vec3(4.1) + 2. * c3 + params.accent_color);
                // bc: base color
                let bc = 2. * sqrt(c1 * cl1 * cl2);
                // te: time effect
                let te = op(params.trap_pow, params.trap_pow, 6., 0., t01);
                // ec: exterior color
                let ec = .5 + .5 * cos(params.col_ext * zd.w + zd.z + params.base_color + PI * 6. * ir + te);
                // bf: blend factor
                let bf = smoothstep(.2, params.trap_s1, ir);
                let pc = mix(bc, ec, bf);
                let tce = op(1., 1., 10., 5., t01);
                // vc: variation color
                let vc = pc * (.5 + .5 * sin(PI * vec3(.5, .7, .9) * ir + tce));
                col += mix(pc, vc, params.trap_c1);
            }
        }
    }
    col = g(col / f32(AA * AA), params.gamma_correction);
    let q = frag.xy / ss;
    col *= .7 + .3 * pow(16. * q.x * q.y * (1. - q.x) * (1. - q.y), .15);
    
    textureStore(output, vec2<i32>(i32(coords.x), i32(coords.y)), vec4(col, 1.0));
}