Skip to content

Instantly share code, notes, and snippets.

@munrocket
Last active April 24, 2024 00:45
Show Gist options
  • Star 81 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save munrocket/f247155fc22ecb8edf974d905c677de1 to your computer and use it in GitHub Desktop.
Save munrocket/f247155fc22ecb8edf974d905c677de1 to your computer and use it in GitHub Desktop.
WGSL 3D SDF Primitives

WGSL 3D SDF Primitives

Revision: 06.08.2023, https://compute.toys/view/407

Sphere - exact

fn sdSphere(p: vec3f, r: f32) -> f32 {
  return length(p) - r;
}

Ellipsoid - bound (not exact)

fn sdEllipsoid(p: vec3f, r: vec3f) -> f32 {
  let k0 = length(p / r);
  let k1 = length(p / (r * r));
  return k0 * (k0 - 1.) / k1;
}

Box - exact

fn sdBox(p: vec3f, b: vec3f) -> f32 {
  let q = abs(p) - b;
  return length(max(q, vec3f(0.))) + min(max(q.x, max(q.y, q.z)), 0.);
}

Round Box - exact

fn sdRoundBox(p: vec3f, b: vec3f, r: f32) -> f32 {
  let q = abs(p) - b;
  return length(max(q, vec3f(0.))) + min(max(q.x,max(q.y, q.z)), 0.) - r;
}

Box Frame - exact

fn sdBoxFrame(p: vec3f, b: vec3f, e: f32) -> f32 {
  let q = abs(p) - b;
  let w = abs(q + e) - e;
  return min(min(
      length(max(vec3f(q.x, w.y, w.z), vec3f(0.))) + min(max(q.x, max(w.y, w.z)), 0.),
      length(max(vec3f(w.x, q.y, w.z), vec3f(0.))) + min(max(w.x, max(q.y, w.z)), 0.)),
      length(max(vec3f(w.x, w.y, q.z), vec3f(0.))) + min(max(w.x, max(w.y, q.z)), 0.));
}

Gyroid - bound

fn sdGyroid(p: vec3f, h: f32) -> f32 {
  return abs(dot(sin(p), cos(p.zxy))) - h;
}
//d = max(sdBox(p, vec3f(.8)), .5 * sdGyroid((p+vec3f(2.)) * 6.5, .2) / 6.5);

Torus - exact

fn sdTorus(p: vec3f, R: f32, r: f32) -> f32 {
  let q = vec2f(length(p.xz) - R, p.y);
  return length(q) - r;
}

Capped Torus - exact

fn sdCappedTorus(p: vec3f, R: f32, r: f32, sincos: vec2f) -> f32 {
  let q = vec3f(abs(p.x), p.y, p.z);
  let k = select(length(q.xy), dot(q.xy, sincos), sincos.y * q.x > sincos.x * q.y);
  return sqrt(dot(q, q) + R * R - 2. * R * k) - r;
}

Link - exact

fn sdLink(p: vec3f, R: f32, r: f32, le: f32) -> f32 {
  let q = vec3f(p.x, max(abs(p.y) - le, 0.), p.z);
  return length(vec2f(length(q.xy) - R, q.z)) - r;
}

Capsule / Line - exact

fn sdCapsule(p: vec3f, a: vec3f, b: vec3f, r: f32) -> f32 {
  let pa = p - a;
  let ba = b - a;
  let h = clamp(dot(pa, ba) / dot(ba, ba), 0., 1.);
  return length(pa - ba * h) - r;
}

Vertical Capsule / Line - exact

fn sdVerticalCapsule(p: vec3f, h: f32, r: f32) -> f32 {
  let q = vec3f(p.x, p.y - clamp(p.y, 0., h), p.z);
  return length(q) - r;
}

Cylinder - exact

fn sdCylinder(p: vec3f, a: vec3f, b: vec3f, r: f32) -> f32 {
  let ba = b - a;
  let pa = p - a;
  let baba = dot(ba, ba);
  let paba = dot(pa, ba);
  let x = length(pa * baba - ba * paba) - r * baba;
  let y = abs(paba - baba * 0.5) - baba * 0.5;
  let x2 = x * x;
  let y2 = y * y * baba;
  let d = x2 * step(0., x) + y2 * step(0., y);
  let d2 = select(d, -min(x2, y2), max(x, y) < 0.);
  return sign(d2) * sqrt(abs(d2)) / baba;
}

Vertical Cylinder - exact

fn sdVerticalCylinder(p: vec3f, h: f32, r: f32) -> f32 {
  let d = abs(vec2f(length(p.xz), p.y)) - vec2f(r, h);
  return min(max(d.x, d.y), 0.) + length(max(d, vec2f(0.)));
}

Rounded Cylinder - exact

fn sdRoundedCylinder(p: vec3f, h: f32, r: f32, re: f32) -> f32 {
  let d = vec2f(length(p.xz) - 2. * r + re, abs(p.y) - h);
  return min(max(d.x, d.y), 0.) + length(max(d, vec2f(0.))) - re;
}

Infinite Cylinder - exact

fn sdInfiniteCylinder(p: vec3f, c: vec3f) -> f32 {
  return length(p.xz - c.xy) - c.z;
}

Cone - exact

fn sdCone(p: vec3f, h: f32, sincos: vec2f) -> f32 {
  // Alternatively pass q instead of (sin(alpha), cos(alpha))
  let q = h * vec2f(sincos.x / sincos.y, -1.);
  let w = vec2f(length(p.xz), p.y);
  let a = w - q * clamp(dot(w,q) / dot(q,q), 0., 1.);
  let b = w - q * vec2f(clamp(w.x / q.x, 0., 1.), 1.);
  let k = sign(q.y);
  let d = min(dot(a, a), dot(b, b));
  let s = max(k * (w.x * q.y - w.y * q.x), k * (w.y - q.y));
  return sqrt(d) * sign(s);
}

Cone - bound (not exact)

fn sdConeBound(p: vec3f, h: f32, sincos: vec2f) -> f32 {
  return max(dot(sincos.yx, vec2f(length(p.xz), p.y)), -h - p.y);
}

Infinite Cone - exact

fn sdInfiniteCone(p: vec3f, sincos: vec2f) -> f32 {
  let q = vec2f(length(p.xz), -p.y);
  let d = length(q - sincos * max(dot(q, sincos), 0.));
  return d * select(-1., 1., q.x * sincos.y - q.y * sincos.x > 0.0);
}

Capped Vertical Cone - exact

fn sdCappedVerticalCone(p: vec3f, h: f32, r1: f32, r2: f32) -> f32 {
  let q = vec2f(length(p.xz), p.y);
  let k1 = vec2f(r2, h);
  let k2 = vec2f(r2 - r1, 2. * h);
  let ca = vec2f(q.x - min(q.x, select(r2, r1, q.y < 0.)), abs(q.y) - h);
  let cb = q - k1 + k2 * clamp(dot(k1 - q, k2) / dot(k2, k2), 0., 1.);
  let s = select(1., -1., cb.x < 0. && ca.y < 0.);
  return s * sqrt(min(dot(ca, ca), dot(cb, cb)));
}

Capped Cone - exact

fn sdCappedCone(p: vec3f, a: vec3f, b: vec3f, ra: f32, rb: f32) -> f32 {
  let rba = rb - ra;
  let baba = dot(b - a, b - a);
  let papa = dot(p - a, p - a);
  let paba = dot(p - a, b - a) / baba;
  let x = sqrt(papa - paba * paba * baba);
  let cax = max(0.0, x - select(rb, ra, paba < 0.5));
  let cay = abs(paba - 0.5) - 0.5;
  let k = rba * rba + baba;
  let f = clamp((rba * (x - ra) + paba * baba) / k, 0.0, 1.0);
  let cbx = x - ra - f * rba;
  let cby = paba - f;
  let s = select(1., -1., cbx < 0.0 && cay < 0.0);
  return s * sqrt(min(cax * cax + cay * cay * baba, cbx * cbx + cby * cby * baba));
}

Round Vertical cone - exact

fn sdRoundVerticalCone(p: vec3f, h: f32, r1: f32, r2: f32) -> f32 {
  let q = vec2f(length(p.xz), p.y);
  let b = (r1 - r2) / h;
  let a = sqrt(1. - b * b);
  let k = dot(q, vec2f(-b, a));
  if (k < 0.) { return length(q) - r1; }
  if (k > a * h) { return length(q - vec2f(0., h)) - r2; }
  return dot(q, vec2f(a, b)) - r1;
}

Round cone - exact

fn sdRoundCone(p: vec3f, a: vec3f, b: vec3f, r1: f32, r2: f32) -> f32 {
  let ba = b - a;
  let l2 = dot(ba, ba);
  let rr = r1 - r2;
  let a2 = l2 - rr * rr;
  let il2 = 1. / l2;

  let pa = p - a;
  let y = dot(pa, ba);
  let z = y - l2;
  let w = pa * l2 - ba * y;
  let x2 = dot(w, w);
  let y2 = y * y * l2;
  let z2 = z * z * l2;

  let k = sign(rr) * rr * rr * x2;
  if (sign(z) * a2 * z2 > k) { return sqrt(x2 + z2) * il2 - r2; }
  if (sign(y) * a2 * y2 < k) { return sqrt(x2 + y2) * il2 - r1; }
  return (sqrt(x2 * a2 * il2) + y * rr) * il2 - r1;
}

Solid Angle - exact

fn sdSolidAngle(p: vec3f, sincos: vec2f, r: f32) -> f32 {
  let q = vec2f(length(p.xz), p.y);
  let l = length(q) - r;
  let m = length(q - sincos * clamp(dot(q, sincos), 0., r));
  return max(l, m * sign(sincos.y * q.x - sincos.x * q.y));
}

Plane - exact

fn sdPlane(p: vec3f, n: vec3f, h: f32) -> f32 {
  // n must be normalized
  return dot(p, n) + h;
}

Octahedron - exact

fn sdOctahedron(p: vec3f, s: f32) -> f32 {
  var q: vec3f = abs(p);
  let m = q.x + q.y + q.z - s;
  if (3. * q.x < m) {q = q.xyz;}
  else {if (3. * q.y < m) {q = q.yzx;}
        else {if (3. * q.z < m) {q = q.zxy;}
              else {return m * 0.57735027;}}}
  let k = clamp(0.5 * (q.z - q.y + s), 0., s);
  return length(vec3f(q.x, q.y - s + k, q.z - k));
}

Octahedron - bound (not exact)

fn sdOctahedronBound(p: vec3f, s: f32) -> f32 {
  let q = abs(p);
  return (q.x + q.y + q.z - s) * 0.57735027;
}

Pyramid - exact

fn sdPyramid(p: vec3f, h: f32) -> f32 {
  let m2 = h * h + 0.25;
  var xz: vec2f = abs(p.xz);
  xz = select(xz, xz.yx, xz[1] > xz[0]);
  xz = xz - vec2f(0.5);

  let q = vec3f(xz[1], h * p.y - 0.5 * xz[0], h * xz[0] + 0.5 * p.y);
  let s = max(-q.x, 0.);
  let t = clamp((q.y - 0.5 * xz[1]) / (m2 + 0.25), 0., 1.);

  let a = m2 * (q.x + s) * (q.x + s) + q.y * q.y;
  let b = m2 * (q.x + 0.5 * t) * (q.x + 0.5 * t) + (q.y - m2 * t) * (q.y - m2 * t);

  let d2 = min(a, b) * step(min(q.y, -q.x * m2 - q.y * 0.5), 0.);
  return sqrt((d2 + q.z * q.z) / m2) * sign(max(q.z, -p.y));
}

Hexagonal Prism - exact

fn sdHexPrism(p: vec3f, h: vec2f) -> f32 {
  let k = vec3f(-0.8660254, 0.5, 0.57735);
  let a = abs(p);
  let v = a.xy - 2. * min(dot(k.xy, a.xy), 0.) * k.xy;
  let d1 = length(v - vec2f(clamp(v.x, -k.z * h.x, k.z * h.x), h.x)) * sign(v.y - h.x);
  let d2 = a.z - h.y;
  return min(max(d1, d2), 0.) + length(max(vec2f(d1, d2), vec2f(0.)));
}

Triangular Prism - bound

fn sdTriPrism(p: vec3f, h: vec2f) -> f32 {
  let q = abs(p);
  return max(q.z - h.y, max(q.x * 0.866025 + p.y * 0.5, -p.y) - h.x * 0.5);
}

Quadratic Bezier - exact

fn sdBezier(p: vec3f, A: vec3f, B: vec3f, C: vec3f) -> vec2f {
  let a = B - A;
  let b = A - 2. * B + C;
  let c = a * 2.;
  let d = A - p;
  let kk = 1. / dot(b, b);
  let kx = kk * dot(a, b);
  let ky = kk * (2. * dot(a, a) + dot(d, b)) / 3.;
  let kz = kk * dot(d, a);

  let p1 = ky - kx * kx;
  let p3 = p1 * p1 * p1;
  let q = kx * (2.0 * kx * kx - 3.0 * ky) + kz;
  var h: f32 = q * q + 4. * p3;

  var res: vec2f;
  if (h >= 0.) {
    h = sqrt(h);
    let x = (vec2f(h, -h) - q) / 2.;
    let uv = sign(x) * pow(abs(x), vec2f(1. / 3.));
    let t = clamp(uv.x + uv.y - kx, 0., 1.);
    let f = d + (c + b * t) * t;
    res = vec2f(dot(f, f), t);
  } else {
    let z = sqrt(-p1);
    let v = acos(q / (p1 * z * 2.)) / 3.;
    let m = cos(v);
    let n = sin(v) * 1.732050808;
    let t = clamp(vec2f(m + m, -n - m) * z - kx, vec2f(0.0), vec2f(1.0));
    let f = d + (c + b * t.x) * t.x;
    var dis: f32 = dot(f, f);
    res = vec2f(dis, t.x);

    let g = d + (c + b * t.y) * t.y;
    dis = dot(g, g);
    res = select(res, vec2f(dis, t.y), dis < res.x);
  }
  res.x = sqrt(res.x);
  return res;
}

Triangle - exact

fn udTriangle(p: vec3f, a: vec3f, b: vec3f, c: vec3f) -> f32 {
  let ba = b - a; let pa = p - a;
  let cb = c - b; let pb = p - b;
  let ac = a - c; let pc = p - c;
  let nor = cross(ba, ac);
   let d1 = ba * clamp(dot(ba, pa) / dot(ba, ba), 0., 1.) - pa;
  let d2 = cb * clamp(dot(cb, pb) / dot(cb, cb), 0., 1.) - pb;
  let d3 = ac * clamp(dot(ac, pc) / dot(ac, ac), 0., 1.) - pc;
   let k0 = min(min(dot(d1, d1), dot(d2, d2)), dot(d3, d3));
  let k1 = dot(nor, pa) * dot(nor, pa) / dot(nor, nor);
  let t = sign(dot(cross(ba, nor), pa)) + sign(dot(cross(cb, nor), pb)) +
      sign(dot(cross(ac, nor), pc));
  return sqrt(select(k0, k1, t < 2.));
}

Quad - exact

fn udQuad(p: vec3f, a: vec3f, b: vec3f, c: vec3f, d: vec3f) -> f32 {
  let ba = b - a; let pa = p - a;
  let cb = c - b; let pb = p - b;
  let dc = d - c; let pc = p - c;
  let ad = a - d; let pd = p - d;
  let nor = cross(ba, ad);
   let d1 = ba * clamp(dot(ba, pa) / dot(ba, ba), 0., 1.) - pa;
  let d2 = cb * clamp(dot(cb, pb) / dot(cb, cb), 0., 1.) - pb;
  let d3 = dc * clamp(dot(dc, pc) / dot(dc, dc), 0., 1.) - pc;
  let d4 = ad * clamp(dot(ad, pd) / dot(ad, ad), 0., 1.) - pd;
   let k0 = min(min(dot(d1, d1), dot(d2, d2)), min(dot(d3, d3), dot(d4, d4)));
  let k1 = dot(nor, pa) * dot(nor, pa) / dot(nor, nor);
  let t = sign(dot(cross(ba, nor), pa)) + sign(dot(cross(cb, nor), pb)) +
      sign(dot(cross(dc, nor), pc)) + sign(dot(cross(ad, nor), pd));
  return sqrt(select(k0, k1, t < 3.));
}

Bunny - bound

fn sdBunny(p: vec3f) -> f32 {
  if (dot(p, p) > 1.) { return length(p) - .8; }
  let q = vec4f(p, 1.);
  let f00=sin(mat4x4f(-3.02,1.95,-3.42,-.6,3.08,.85,-2.25,-.24,-.29,1.16,-3.74,2.89,-.71,4.5,-3.24,-3.5)*q);
  let f01=sin(mat4x4f(-.4,-3.61,3.23,-.14,-.36,3.64,-3.91,2.66,2.9,-.54,-2.75,2.71,7.02,-5.41,-1.12,-7.41)*q);
  let f02=sin(mat4x4f(-1.77,-1.28,-4.29,-3.2,-3.49,-2.81,-.64,2.79,3.15,2.14,-3.85,1.83,-2.07,4.49,5.33,-2.17)*q);
  let f03=sin(mat4x4f(-.49,.68,3.05,.42,-2.87,.78,3.78,-3.41,-2.65,.33,.07,-.64,-3.24,-5.9,1.14,-4.71)*q);
  let f10=sin(mat4x4f(-.34,.06,-.59,-.76,.1,-.19,-.12,.44,.64,-.02,-.26,.15,-.16,.21,.91,.15)*f00+
      mat4x4f(.01,.54,-.77,.11,.06,-.14,.43,.51,-.18,.08,.39,.2,.33,-.49,-.1,.19)*f01+
      mat4x4f(.27,.22,.43,.53,.18,-.17,.23,-.64,-.14,.02,-.1,.16,-.13,-.06,-.04,-.36)*f02+
      mat4x4f(-.13,.29,-.29,.08,1.13,.02,-.83,.32,-.32,.04,-.31,-.16,.14,-.03,-.2,.39)*f03+
      vec4f(.73,-4.28,-1.56,-1.8))+f00;
  let f11=sin(mat4x4f(-1.11,.55,-.12,-1.00,.16,.15,-.3,.31,-.01,.01,.31,-.42,-.29,.38,-.04,.71)*f00+
      mat4x4f(.96,-.02,.86,.52,-.14,.6,.44,.43,.02,-.15,-.49,-.05,-.06,-.25,-.03,-.22)*f01+
      mat4x4f(.52,.44,-.05,-.11,-.56,-.1,-.61,-.4,-.04,.55,.32,-.07,-.02,.28,.26,-.49)*f02+
      mat4x4f(.02,-.32,.06,-.17,-.59,.00,-.24,.6,-.06,.13,-.21,-.27,-.12,-.14,.58,-.55)*f03+
      vec4f(-2.24,-3.48,-.8,1.41))+f01;
  let f12=sin(mat4x4f(.44,-.06,-.79,-.46,.05,-.6,.3,.36,.35,.12,.02,.12,.4,-.26,.63,-.21)*f00+
      mat4x4f(-.48,.43,-.73,-.4,.11,-.01,.71,.05,-.25,.25,-.28,-.2,.32,-.02,-.84,.16)*f01+
      mat4x4f(.39,-.07,.9,.36,-.38,-.27,-1.86,-.39,.48,-.2,-.05,.1,-.00,-.21,.29,.63)*f02+
      mat4x4f(.46,-.32,.06,.09,.72,-.47,.81,.78,.9,.02,-.21,.08,-.16,.22,.32,-.13)*f03+
      vec4f(3.38,1.2,.84,1.41))+f02;
  let f13=sin(mat4x4f(-.41,-.24,-.71,-.25,-.24,-.75,-.09,.02,-.27,-.42,.02,.03,-.01,.51,-.12,-1.24)*f00+
      mat4x4f(.64,.31,-1.36,.61,-.34,.11,.14,.79,.22,-.16,-.29,-.70,.02,-.37,.49,.39)*f01+
      mat4x4f(.79,.47,.54,-.47,-1.13,-.35,-1.03,-.22,-.67,-.26,.1,.21,-.07,-.73,-.11,.72)*f02+
      mat4x4f(.43,-.23,.13,.09,1.38,-.63,1.57,-.2,.39,-.14,.42,.13,-.57,-.08,-.21,.21)*f03+
      vec4f(-.34,-3.28,.43,-.52))+f03;
  let f20=sin(mat4x4f(-.72,.23,-.89,.52,.38,.19,-.16,-.88,.26,-.37,.09,.63,.29,-.72,.3,-.95)*f10+
      mat4x4f(-.22,-.51,-.42,-.73,-.32,.00,-1.03,1.17,-.2,-.03,-.13,-.16,-.41,.09,.36,-.84)*f11+
      mat4x4f(-.21,.01,.33,.47,.05,.2,-.44,-1.04,.13,.12,-.13,.31,.01,-.34,.41,-.34)*f12+
      mat4x4f(-.13,-.06,-.39,-.22,.48,.25,.24,-.97,-.34,.14,.42,-.00,-.44,.05,.09,-.95)*f13+
      vec4f(.48,.87,-.87,-2.06))/1.4+f10;
  let f21=sin(mat4x4f(-.27,.29,-.21,.15,.34,-.23,.85,-.09,-1.15,-.24,-.05,-.25,-.12,-.73,-.17,-.37)*f10+
      mat4x4f(-1.11,.35,-.93,-.06,-.79,-.03,-.46,-.37,.6,-.37,-.14,.45,-.03,-.21,.02,.59)*f11+
      mat4x4f(-.92,-.17,-.58,-.18,.58,.6,.83,-1.04,-.8,-.16,.23,-.11,.08,.16,.76,.61)*f12+
      mat4x4f(.29,.45,.3,.39,-.91,.66,-.35,-.35,.21,.16,-.54,-.63,1.1,-.38,.2,.15)*f13+
      vec4f(-1.72,-.14,1.92,2.08))/1.4+f11;
  let f22=sin(mat4x4f(1.00,.66,1.3,-.51,.88,.25,-.67,.03,-.68,-.08,-.12,-.14,.46,1.15,.38,-.1)*f10+
      mat4x4f(.51,-.57,.41,-.09,.68,-.5,-.04,-1.01,.2,.44,-.6,.46,-.09,-.37,-1.3,.04)*f11+
      mat4x4f(.14,.29,-.45,-.06,-.65,.33,-.37,-.95,.71,-.07,1.00,-.6,-1.68,-.2,-.00,-.7)*f12+
      mat4x4f(-.31,.69,.56,.13,.95,.36,.56,.59,-.63,.52,-.3,.17,1.23,.72,.95,.75)*f13+
      vec4f(-.9,-3.26,-.44,-3.11))/1.4+f12;
  let f23=sin(mat4x4f(.51,-.98,-.28,.16,-.22,-.17,-1.03,.22,.7,-.15,.12,.43,.78,.67,-.85,-.25)*f10+
      mat4x4f(.81,.6,-.89,.61,-1.03,-.33,.6,-.11,-.06,.01,-.02,-.44,.73,.69,1.02,.62)*f11+
      mat4x4f(-.1,.52,.8,-.65,.4,-.75,.47,1.56,.03,.05,.08,.31,-.03,.22,-1.63,.07)*f12+
      mat4x4f(-.18,-.07,-1.22,.48,-.01,.56,.07,.15,.24,.25,-.09,-.54,.23,-.08,.2,.36)*f13+
      vec4f(-1.11,-4.28,1.02,-.23))/1.4+f13;
  return dot(f20,vec4f(.09,.12,-.07,-.03))+dot(f21,vec4f(-.04,.07,-.08,.05))+
      dot(f22,vec4f(-.01,.06,-.02,.07))+dot(f23,vec4f(-.05,.07,.03,.04))- 0.16;
}

Boolean operations with primitives

Union, Subtraction, Intersection - exact (outside), bound, bound

fn opUnion(d1: f32, d2: f32) -> f32 { return min(d1, d2); }

fn opSubtract(d1: f32, d2: f32) -> f32 { return max(d1, -d2); }

fn opIntersect(d1: f32, d2: f32) -> f32 { return max(d1, d2); }

Chamfer Union, Chamfer Subtraction, Chamfer Intersection - bound, bound, bound

fn opChamferUnion(d1: f32, d2: f32, r: f32) -> f32 {
  return min(min(d1, d2), (d1 - r + d2) * 0.5);
}

fn opChamferSubtract(d1: f32, d2: f32, r: f32) -> f32 {
  return max(max(d1, -d2), (d1 + r - d2) * 0.5);
}

fn opChamferIntersect(d1: f32, d2: f32, r: f32) -> f32 {
  return max(max(d1, d2), (d1 + r + d2) * 0.5);
}

Blend Union, Blend Subtraction, Blend Intersection - bound, bound, bound

fn opSmoothUnion(d1: f32, d2: f32, k: f32) -> f32 {
  let h = clamp(0.5 + 0.5 * (d2 - d1) / k, 0., 1.);
  return mix(d2, d1, h) - k * h * (1. - h);
}

fn opSmoothSubtract(d1: f32, d2: f32, k: f32) -> f32 {
  let h = clamp(0.5 - 0.5 * (d1 + d2) / k, 0., 1.);
  return mix(d1, -d2, h) + k * h * (1. - h);
}

fn opSmoothIntersect(d1: f32, d2: f32, k: f32) -> f32 {
  let h = clamp(0.5 - 0.5 * (d2 - d1) / k, 0., 1.);
  return mix(d2, d1, h) + k * h * (1. - h);
}

Displacement

Displacement - bound (not exact)

fn opDisplace(d1: f32, d2: f32) -> f32 {
  return d1 + d2;
}
//let d = opDisplace(sdfPrimitive3d(p), displacement3d(p));

Twist - bound

fn opTwist(p: vec3f, k: f32) -> vec3f {
  let s = sin(k * p.y);
  let c = cos(k * p.y);
  let m = mat2x2<f32>(vec2f(c, s), vec2f(-s, c));
  return vec3f(m * p.xz, p.y);
}
//let d = sdfPrimitive3d(opTwist(p, k));

Bend - bound

fn opCheapBend(p: vec3f, k: f32) -> vec3f {
  let s = sin(k * p.x);
  let c = cos(k * p.x);
  let m = mat2x2<f32>(vec2f(c, s), vec2f(-s, c));
  return vec3f(m * p.xy, p.z);
}
//let d = sdfPrimitive3d(opCheapBend(p, k));

Positioning

Translate - exact

fn opTranslate(p: vec3f, t: vec3f) -> vec3f {
  return p - t;
}
//let d = sdfPrimitive3d(opTranslate(p, t));

90 degree rotation - exact

fn op90RotateX(p: vec3f) -> vec3f {
  return vec3f(p.x, p.z, -p.y);
}

fn op90RotateY(p: vec3f) -> vec3f {
  return vec3f(-p.z, p.y, p.x);
}

fn op90RotateZ(p: vec3f) -> vec3f {
  return vec3f(p.y, -p.x, p.z);
}
//let d = sdfPrimitive3d(op90RotateZ(p));

Rotation around axis - exact

fn opRotateX(p: vec3f, a: f32) -> vec3f {
  let s = sin(a); let c = cos(a);
  return vec3f(p.x, c * p.y + s * p.z, -s * p.y + c * p.z);
}

fn opRotateY(p: vec3f, a: f32) -> vec3f {
  let s = sin(a); let c = cos(a);
  return vec3f(c * p.x - s * p.z, p.y, s * p.x + c * p.z);
}

fn opRotateZ(p: vec3f, a: f32) -> vec3f {
  let s = sin(a); let c = cos(a);
  return vec3f(c * p.x + s * p.y, -s * p.x + c * p.y, p.z);
}
//let d = sdfPrimitive3d(opRotateY(p, a));

Rotation around free axis - exact

fn opRotateE(p: vec3f, e: vec3f, a: f32) -> vec3f {
  let c = cos(a);
  return dot(e, p) * (1. - c) * e - cross(e, p) * sin(a) + c * p;
}
//let d = sdfPrimitive3d(opRotateE(p, normalize(vec3f(1.,0.,.5)), a));

Scale - exact

fn opScale(p: vec3f, s: f32) -> vec3f {
  return p / s;
}
//let d = sdfPrimitive3d(opScale(p, s)) * s;

Free transformation - exact

fn opTransform(p: vec3f, transform: mat4x4<f32>) -> vec3f {
  let q = inverse(transform) * vec4f(p, 1.);
}
//let d = sdfPrimitive3d(opTransform(p, transform)) * determinant(transform);

// OR
//let d = sdfPrimitive3d(opScale(opRotateE(opTranslate(p, t), e, a), s)) * s;

Symmetry - exact

fn opSymmetryX(p: vec3f) -> vec3f { return vec3f(abs(p.x), p.y, p.z); }

fn opSymmetryY(p: vec3f) -> vec3f { return vec3f(p.x, abs(p.y), p.z); }

fn opSymmetryZ(p: vec3f) -> vec3f { return vec3f(p.x, p.y, abs(p.z)); }
//let d = sdfPrimitive3d(opSymmetryX(p));

Infinite Repetition - exact

fn opInfArray(p: vec3f, c: vec3f) -> vec3f {
  return p - c * round(p / c);
}
//let d = sdfPrimitive3d(opInfArray(p, c));

Finite Repetition - exact

fn opLimArray(p: vec3f, c: f32, lim: vec3f) -> vec3f {
  return p - c * clamp(round(p / c), -lim, lim);
}
//let d = sdfPrimitive3d(opLimArray(p, c));

Primitive alterations

Elongation - exact

fn opElongate(p: vec3f, h: vec3f) -> vec3f {
  return p - clamp(p, -h, h);
}
//let d = sdfPrimitive3d(opElongateFast(p, h));

fn opElongateCorrect(p: vec3f, h: vec3f) -> vec4f {
  let q = abs(p) - h;
  let sgn = 2. * step(vec3f(0.), p) - vec3f(1.);
  return vec4f(sgn * max(q, vec3f(0.)), min(max(q.x, max(q.y, q.z)), 0.));
}
//let p2 = opElongateCorrect(p, h);
//let d = p2.w + sdfPrimitive3d(p2.xyz);

Rounding - exact

fn opRound(p: vec3f, r: f32) -> f32 {
  return sdfPrimitive3d(p) - r;
}

Onion - exact

fn opOnion(d: f32, thickness: f32) -> f32 {
  return abs(d) - thickness;
}
//let d = opOnion(sdfPrimitive3d(p), thickness);

Extrusion from 2D SDF - exact

fn opExtrusion(d: f32, z: f32, h: f32) -> f32 {
  let w = vec2f(d, abs(z) - h);
  return min(max(w.x, w.y), 0.) + length(max(w, vec2f(0.)));
}
//let d = opExtrusion(sdfPrimitive2d(p.xy), p.z, h));

Revolution from 2D SDF - exact

fn opRevolution(p: vec3f, o: f32) -> vec2f {
  return vec2f(length(p.xz) - o, p.y);
}
//let d = sdfPrimitive2d(opRevolution(p, h));

Change metric - bound

fn length4(p: vec3f) -> f32 {
  var q: vec3f = p * p;
  q = q * q;
  return sqrt(sqrt(q.x + q.y + q.z));
}

fn length6(p: vec3f) -> f32 {
  var q: vec3f = p * p * p;
  q = q * q;
  return pow(q.x + q.y + q.z, 1. / 6.);
}

fn length8(p: vec3f) -> f32 {
  var q: vec3f = p * p;
  q = q * q; q = q * q;
  return pow(q.x + q.y + q.z, 1. / 8.);
}

MIT License. © 2020 Inigo Quilez, Johann Korndörfer, Martijn Steinrucken, Blackle Mori, Munrocket

@HannesGitH
Copy link

🤩
any chance you could provide the source used to generate the images?

i tried on my own and only stumbled accross this gist as i cant seem to find any way to iterate through a buffer (of sdf primitives to get the distance) in wgsl

@munrocket
Copy link
Author

munrocket commented Aug 6, 2023

You can check online example on compute.toys. If it is not enough watch youtube or read paper.

@HannesGitH
Copy link

i had no problem understanding ray marching but using wgpu itself.
but my question wasnt formulated very well and even i dont see what exactly my problem was back then :D

anyway thanks for ur reply and providing the (2D) compute toy

i think this is a great starting point for learning webgpu!

@munrocket
Copy link
Author

I am started with WGSL, here simple hello webgpu example. Other tutorials also exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment