Skip to content

Rotating Cube

Adapted from Shadertoy "Just another cube" by mrangeRotating Cube

Click to view code
wgsl
// ——— module-scope state ———
var<private> R: mat2x2<f32>;
var<private> d: f32 = 1.0;
var<private> z: f32 = 0.0;
var<private> G: f32 = 9.0;
const M: f32 = 1e-3;

// ——— SDF + glow function ———
fn D(p: vec3<f32>) -> f32 {
  var q = p;
  // Rotate in XY plane
  let rot1 = R * q.xy;
  q.x = rot1.x; q.y = rot1.y;
  // Then rotate that result in XZ plane
  let rot2 = R * vec2<f32>(q.x, q.z);
  q.x = rot2.x; q.z = rot2.y;

  let S = sin(123.0 * q);
  // superquadric distance
  let base = pow(dot(q*q*q*q, q*q*q*q), 0.125) - 0.5;
  // subtract surface detail
  d = base - pow(1.0 + S.x * S.y * S.z, 8.0) / 1e5;
  // track glow
  G = min(G, max(abs(length(q) - 0.6), d));
  return d;
}

@fragment
fn main(@builtin(position) fragCoord: vec4<f32>) -> @location(0) vec4<f32> {
  // 1) pull in injected uniforms
  let r = iResolution;   // vec3<f32>, .xy = screen size
  let t = iTime;         // f32

  // 2) ShaderToy-style ray direction in *pixel* units
  let dx = fragCoord.x - 0.5 * r.x;
  let dy = fragCoord.y - 0.5 * r.y;
  let I  = normalize(vec3<f32>(dx, dy, r.y));

  // 3) glow color & rotation
  let B     = vec3<f32>(1.0, 2.0, 9.0) * M;
  let angle = 0.3 * t;
  R = mat2x2<f32>(
    cos(angle), -sin(angle),
    sin(angle),  cos(angle)
  );

  // 4) raymarch loop from camera at z = –4
  let camPos = vec3<f32>(0.0, 0.0, -4.0);
  z = 0.0; d = 1.0; G = 9.0;
  loop {
    let P = camPos + z * I;
    if (!(z < 9.0 && d > M)) { break; }
    z += D(P);
  }

  // 5) shading
  var O = vec3<f32>(0.0);
  if (z < 9.0) {
    // estimate normal via central differences
    var N = vec3<f32>(0.0);
    for (var i = 0u; i < 3u; i = i + 1u) {
      var off = vec3<f32>(0.0);
      off[i] = M;
      let P = camPos + z * I;
      N[i] = D(P + off) - D(P - off);
    }
    N = normalize(N);

    let fres = 1.0 + dot(N, I);
    let refl = reflect(I, N);
    let hit  = camPos + z * I;
    let Cxy  = (hit + refl * ((5.0 - hit.y) / abs(refl.y))).xz;

    O = fres*fres * select(
      exp(-2.0 * length(Cxy)) * (B/M - 1.0),
      5e2 * smoothstep(5.0, 4.0, sqrt(dot(Cxy, Cxy)) + 1.0)
           * (sqrt(dot(Cxy, Cxy)) + 1.0) * B,
      refl.y > 0.0
    ) + pow(1.0 + O.y, 5.0) * B;
  }

  // 6) tonemap & output
  return vec4<f32>(sqrt(O + B/G), 1.0);
}

Cube Smash

Adapted from Shadertoy "SmashEffect" by dilaCube smash

Click to view code
wgsl
// --- Utility functions ---
fn rot(t: f32) -> mat2x2<f32> {
    return mat2x2<f32>(
        cos(t),  sin(t),
       -sin(t),  cos(t)
    );
}

// iq’s rounded box SDF
fn udRoundBox(p: vec3<f32>, b: vec3<f32>, r: f32) -> f32 {
    let q = max(abs(p) - b, vec3<f32>(0.0));
    return length(q) - r;
}

// compute the 3-phase “times” vector
fn times() -> vec3<f32> {
    let gt = fract(iTime * 0.5) * 3.0;
    let a = clamp(gt - 0.0, 0.0, 1.0);
    let b = clamp(gt - 1.0, 0.0, 1.0);
    let c = clamp(gt - 2.0, 0.0, 1.0);
    return vec3<f32>(a, b, c);
}

// --- corrected map() ---
fn map(p: vec3<f32>) -> f32 {
    // copy into a mutable local
    var pos = p;

    let height = 1.0;
    let ground = pos.y + height;

    let pt = times();
    var pound = 1.0 - pow(1.0 - pt.y, 2.0) - pow(pt.z, 32.0);
    pound = pound * 2.0;

    let srot = smoothstep(0.0, 1.0, (pt.y + pt.z) * 0.5);
    let mrot = rot(-0.3 + srot * 3.1415926);

    // rotate XZ
    let xz = mrot * vec2<f32>(pos.x, pos.z);
    pos = vec3<f32>(xz.x, pos.y, xz.y);

    let boxOff = vec3<f32>(0.0, pound, 0.0);
    let boxDist = udRoundBox(pos - boxOff, vec3<f32>(height) * 0.5, height * 0.25);

    return min(ground, boxDist);
}

// simple ray-march
fn trace(o: vec3<f32>, r: vec3<f32>) -> f32 {
    var t: f32 = 0.0;
    for (var i: i32 = 0; i < 32; i = i + 1) {
        let p = o + r * t;
        let d = map(p);
        t = t + d * 0.5;
    }
    return t;
}

// ray-plane intersection
fn rayPlane(o: vec3<f32>, r: vec3<f32>, p: vec3<f32>, n: vec3<f32>) -> f32 {
    return dot(p - o, n) / dot(r, n);
}

// triple-axis texture
fn _texture(p: vec3<f32>) -> vec3<f32> {
    let ta = textureSample(iChannel2, iChannel2Sampler, p.xz).xyz;
    let tb = textureSample(iChannel2, iChannel2Sampler, p.yz).xyz;
    let tc = textureSample(iChannel2, iChannel2Sampler, p.xy).xyz;

    return (ta * ta + tb * tb + tc * tc) / 3.0;
}

// compute normal via finite diff
fn normal(p: vec3<f32>) -> vec3<f32> {
    let eps = 0.01;
    let dx = vec3<f32>(eps, 0.0, 0.0);
    let dy = vec3<f32>(0.0, eps, 0.0);
    let dz = vec3<f32>(0.0, 0.0, eps);
    return normalize(vec3<f32>(
        map(p + dx) - map(p - dx),
        map(p + dy) - map(p - dy),
        map(p + dz) - map(p - dz)
    ));
}

// volumetric smoke
fn smoke(o: vec3<f32>, r: vec3<f32>, f: vec3<f32>, tMax: f32) -> vec3<f32> {
    let tms = times();
    var sm: vec3<f32> = vec3<f32>(0.0);
    let steps: i32 = 32;

    for (var i: i32 = 0; i < steps; i = i + 1) {
        let j = f32(i) / f32(steps);
        let bout = 1.0 + tms.x;
        var p = vec3<f32>(cos(j * 6.2831853), 0.0, sin(j * 6.2831853)) * bout;
        p.y = -1.0;

        let pt = rayPlane(o, r, p, f);
        let pp = o + r * pt;
        let cd = length(pp - p);
        let uv = (pp - p).xy * 0.1 + vec2<f32>(j, j) * 2.0;

        // **always** sample in uniform control flow
        let rawTex = textureSample(iChannel1, iChannel1Sampler, uv).xyz;
        let tex = vec3<f32>(
            (rawTex.x * rawTex.x + rawTex.y * rawTex.y + rawTex.z * rawTex.z) / 3.0
        );

        // build a 0/1 mask without branching
        let inside = select(
            0.0,
            1.0,
            (pt >= 0.0) && (pt <= tMax)
        );

        // now all math is branchless
        var part = tex / (1.0 + cd * cd * 10.0 * tms.x);
        part = part * clamp(abs(tMax - pt), 0.0, 1.0);
        part = part / (1.0 + pt * pt);
        part = part * clamp(pt, 0.0, 1.0);
        part = part * inside;

        sm = sm + part;
    }

    return sm * (1.0 - smoothstep(0.0, 1.0, tms.x));
}

// main shading
fn shade(o: vec3<f32>, r: vec3<f32>, f: vec3<f32>, w: vec3<f32>, t: f32) -> vec3<f32> {
    var tuv = w;
    if (tuv.y > -0.85) {
        let pt = times();
        let srot = smoothstep(0.0, 1.0, (pt.y + pt.z) * 0.5);
        let mrot = rot(-0.3 + srot * 3.1415926);
        let xz = mrot * vec2<f32>(tuv.x, tuv.z);
        tuv.x = xz.x; tuv.z = xz.y;

        var pound = 1.0 - pow(1.0 - pt.y, 2.0) - pow(pt.z, 32.0);
        pound = pound * 2.0;
        tuv.y = tuv.y - pound;
    }

    let tex = _texture(tuv * 0.5);
    let sn  = normal(w);
    let ground = vec3<f32>(1.0);
    let sky    = vec3<f32>(1.0, 0.9, 0.9);
    let slight = mix(ground, sky, 0.5 + 0.5 * sn.y);

    // ambient occlusion hack
    var aoc: f32 = 0.0;
    let aocs = 8;
    for (var i: i32 = 0; i < aocs; i = i + 1) {
        let p = w - r * f32(i) * 0.2;
        aoc = aoc + map(p) * 0.5;
    }
    aoc = 1.0 - 1.0 / (1.0 + (aoc / f32(aocs)));

    let fog = 1.0 / (1.0 + t * t * 0.01);
    let smk = smoke(o, r, f, t);
    let fakeOcc = 0.5 + 0.5 * pow(1.0 - times().y, 4.0);

    var col = slight * tex * aoc + smk * sky;
    col = mix(col * fakeOcc, sky, 1.0 - fog);
    return col;
}

@fragment
// fn main(@location(0) fragCoord: vec2<f32>) -> @location(0) vec4<f32> {
fn main(@builtin(position) fragCoord: vec4<f32>) -> @location(0) vec4<f32> {
    // let uv0 = fragCoord / iResolution;
    let uv0 = vec2<f32>(
        1.0 - fragCoord.x / iResolution.x, // horizontal flip (X)
        1.0 - fragCoord.y / iResolution.y  // vertical flip (Y)
    );
    var uv = uv0 * 2.0 - vec2<f32>(1.0);
    uv.x = uv.x * (iResolution.x / iResolution.y);

    var r = normalize(vec3<f32>(uv, 0.8 - dot(uv, uv) * 0.2));
    var o = vec3<f32>(0.0, 0.125, -1.5);
    let f = vec3<f32>(0.0, 0.0, 1.0);

    let pt = times();
    let shake = pow(1.0 - pt.x, 4.0);
    var smack = textureSample(iChannel0, iChannel0Sampler, vec2<f32>(pt.x, 0.5)).xyz * 2.0 - vec3<f32>(1.0);
    smack = smack * shake;

    o.x = o.x + smack.x * shake * 0.25;
    o.z = o.z + smack.y * shake * 0.1;

    let rot2 = rot(0.3 + smack.z * shake * 0.1);
    let rxy = rot2 * r.xy;
    r.x = rxy.x; r.y = rxy.y;

    let t = trace(o, r);
    let w = o + r * t;
    let col = shade(o, r, f, w, t);

    return vec4<f32>(sqrt(col), 1.0);
}