- Build a sphere tracer with WebGPU (paper, paper2, youtube)
- Create model with sdf functions from here
- Add light and shadows
- ???
- PROFIT
This code tested in Chrome and Firefox, should work on PC too.
Function select
can work differently in browsers.
fn sdSphere(p: vec3<f32>, r: f32) -> f32 {
return length(p) - r;
}
fn sdEllipsoid(p: vec3<f32>, r: vec3<f32>) -> f32 {
let k0 = length(p / r);
let k1 = length(p / (r * r));
return k0 * (k0 - 1.) / k1;
}
fn sdBox(p: vec3<f32>, b: vec3<f32>) -> f32 {
let q = abs(p) - b;
return length(max(q, vec3<f32>(0.))) + min(max(q.x, max(q.y, q.z)), 0.);
}
fn sdRoundBox(p: vec3<f32>, b: vec3<f32>, r: f32) -> f32 {
let q = abs(p) - b;
return length(max(q, vec3<f32>(0.))) + min(max(q.x,max(q.y, q.z)), 0.) - r;
}
fn sdBoxFrame(p: vec3<f32>, b: vec3<f32>, e: f32) -> f32 {
let q = abs(p) - b;
let w = abs(q + e) - e;
return min(min(
length(max(vec3<f32>(q.x, w.y, w.z), vec3<f32>(0.))) + min(max(q.x, max(w.y, w.z)), 0.),
length(max(vec3<f32>(w.x, q.y, w.z), vec3<f32>(0.))) + min(max(w.x, max(q.y, w.z)), 0.)),
length(max(vec3<f32>(w.x, w.y, q.z), vec3<f32>(0.))) + min(max(w.x, max(w.y, q.z)), 0.));
}
fn sdGyroid(p: vec3<f32>, h: f32) -> f32 {
return abs(dot(sin(p), cos(p.zxy))) - h;
}
fn sdTorus(p: vec3<f32>, R: f32, r: f32) -> f32 {
let q = vec2<f32>(length(p.xz) - R, p.y);
return length(q) - r;
}
fn sdCappedTorus(p: vec3<f32>, R: f32, r: f32, sincos: vec2<f32>) -> f32 {
let q = vec3<f32>(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;
}
fn sdLink(p: vec3<f32>, R: f32, r: f32, le: f32) -> f32 {
let q = vec3<f32>(p.x, max(abs(p.y) - le, 0.), p.z);
return length(vec2<f32>(length(q.xy) - R, q.z)) - r;
}
fn sdCapsule(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, 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;
}
fn sdVerticalCapsule(p: vec3<f32>, h: f32, r: f32) -> f32 {
let q = vec3<f32>(p.x, p.y - clamp(p.y, 0., h), p.z);
return length(q) - r;
}
fn sdCylinder(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, 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;
}
fn sdVerticalCylinder(p: vec3<f32>, h: f32, r: f32) -> f32 {
let d = abs(vec2<f32>(length(p.xz), p.y)) - vec2<f32>(r, h);
return min(max(d.x, d.y), 0.) + length(max(d, vec2<f32>(0.)));
}
fn sdRoundedCylinder(p: vec3<f32>, h: f32, r: f32, re: f32) -> f32 {
let d = vec2<f32>(length(p.xz) - 2. * r + re, abs(p.y) - h);
return min(max(d.x, d.y), 0.) + length(max(d, vec2<f32>(0.))) - re;
}
fn sdInfiniteCylinder(p: vec3<f32>, c: vec3<f32>) -> f32 {
return length(p.xz - c.xy) - c.z;
}
fn sdCone(p: vec3<f32>, h: f32, sincos: vec2<f32>) -> f32 {
// Alternatively pass q instead of (sin(alpha), cos(alpha))
let q = h * vec2<f32>(sincos.x / sincos.y, -1.);
let w = vec2<f32>(length(p.xz), p.y);
let a = w - q * clamp(dot(w,q) / dot(q,q), 0., 1.);
let b = w - q * vec2<f32>(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);
}
fn sdConeBound(p: vec3<f32>, h: f32, sincos: vec2<f32>) -> f32 {
return max(dot(sincos.yx, vec2<f32>(length(p.xz), p.y)), -h - p.y);
}
fn sdInfiniteCone(p: vec3<f32>, sincos: vec2<f32>) -> f32 {
let q = vec2<f32>(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);
}
fn sdCappedVerticalCone(p: vec3<f32>, h: f32, r1: f32, r2: f32) -> f32 {
let q = vec2<f32>(length(p.xz), p.y);
let k1 = vec2<f32>(r2, h);
let k2 = vec2<f32>(r2 - r1, 2. * h);
let ca = vec2<f32>(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)));
}
fn sdCappedCone(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, 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));
}
fn sdRoundVerticalCone(p: vec3<f32>, h: f32, r1: f32, r2: f32) -> f32 {
let q = vec2<f32>(length(p.xz), p.y);
let b = (r1 - r2) / h;
let a = sqrt(1. - b * b);
let k = dot(q, vec2<f32>(-b, a));
if (k < 0.) { return length(q) - r1; }
if (k > a * h) { return length(q - vec2<f32>(0., h)) - r2; }
return dot(q, vec2<f32>(a, b)) - r1;
}
fn sdRoundCone(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, 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;
}
fn sdSolidAngle(p: vec3<f32>, sincos: vec2<f32>, r: f32) -> f32 {
let q = vec2<f32>(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));
}
fn sdPlane(p: vec3<f32>, n: vec3<f32>, h: f32) -> f32 {
// n must be normalized
return dot(p, n) + h;
}
fn sdOctahedron(p: vec3<f32>, s: f32) -> f32 {
var q: vec3<f32> = 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(vec3<f32>(q.x, q.y - s + k, q.z - k));
}
fn sdOctahedronBound(p: vec3<f32>, s: f32) -> f32 {
let q = abs(p);
return (q.x + q.y + q.z - s) * 0.57735027;
}
fn sdPyramid(p: vec3<f32>, h: f32) -> f32 {
let m2 = h * h + 0.25;
var xz: vec2<f32> = abs(p.xz);
xz = select(xz, xz.yx, xz[1] > xz[0]);
xz = xz - vec2<f32>(0.5);
let q = vec3<f32>(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));
}
fn sdHexPrism(p: vec3<f32>, h: vec2<f32>) -> f32 {
let k = vec3<f32>(-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 - vec2<f32>(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(vec2<f32>(d1, d2), vec2<f32>(0.)));
}
fn sdTriPrism(p: vec3<f32>, h: vec2<f32>) -> 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);
}
fn sdBezier(p: vec3<f32>, A: vec3<f32>, B: vec3<f32>, C: vec3<f32>) -> vec2<f32> {
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: vec2<f32>;
if (h >= 0.) {
h = sqrt(h);
let x = (vec2<f32>(h, -h) - q) / 2.;
let uv = sign(x) * pow(abs(x), vec2<f32>(1. / 3.));
let t = clamp(uv.x + uv.y - kx, 0., 1.);
let f = d + (c + b * t) * t;
res = vec2<f32>(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(vec2<f32>(m + m, -n - m) * z - kx, vec2<f32>(0.0), vec2<f32>(1.0));
let f = d + (c + b * t.x) * t.x;
var dis: f32 = dot(f, f);
res = vec2<f32>(dis, t.x);
let g = d + (c + b * t.y) * t.y;
dis = dot(g, g);
res = select(res, vec2<f32>(dis, t.y), dis < res.x);
}
res.x = sqrt(res.x);
return res;
}
fn udTriangle(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, c: vec3<f32>) -> 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.));
}
fn udQuad(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, c: vec3<f32>, d: vec3<f32>) -> 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.));
}
fn opUnion(d1: f32, d2: f32) -> f32 { return min(d1, d2); }
fn opSubtraction(d1: f32, d2: f32) -> f32 { return max(d1, -d2); }
fn opIntersection(d1: f32, d2: f32) -> f32 { return max(d1, d2); }
fn opUnionChamfer(d1: f32, d2: f32, r: f32) -> f32 {
return min(min(d1, d2), (d1 - r + d2) * 0.5);
}
fn opSubtractionChamfer(d1: f32, d2: f32, r: f32) -> f32{
return max(max(d1, -d2), (d1 + r - d2) * 0.5);
}
fn opIntersectionChamfer(d1: f32, d2: f32, r: f32) -> f32 {
return max(max(d1, d2), (d1 + r + d2) * 0.5);
}
fn opUnionBlend(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 opSubtractionBlend(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 opIntersectionBlend(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 opDisplace(d1: f32, d2: f32) -> f32 {
return d1 + d2;
}
//let d = opDisplace(sdfPrimitive3d(p), displacement3d(p));
fn opTwist(p: vec3<f32>, k: f32) -> vec3<f32> {
let s = sin(k * p.y);
let c = cos(k * p.y);
let m = mat2x2<f32>(vec2<f32>(c, s), vec2<f32>(-s, c));
return vec3<f32>(m * p.xz, p.y);
}
//let d = sdfPrimitive3d(opTwist(p, k));
fn opCheapBend(p: vec3<f32>, k: f32) -> vec3<f32> {
let s = sin(k * p.x);
let c = cos(k * p.x);
let m = mat2x2<f32>(vec2<f32>(c, s), vec2<f32>(-s, c));
return vec3<f32>(m * p.xy, p.z);
}
//let d = sdfPrimitive3d(opCheapBend(p, k));
fn opTranslate(p: vec3<f32>, t: vec3<f32>) -> vec3<f32> {
return p - t;
}
//let d = sdfPrimitive3d(opTranslate(p, t));
fn op90RotateX(p: vec3<f32>) -> vec3<f32> {
return vec3<f32>(p.x, p.z, -p.y);
}
fn op90RotateY(p: vec3<f32>) -> vec3<f32> {
return vec3<f32>(-p.z, p.y, p.x);
}
fn op90RotateZ(p: vec3<f32>) -> vec3<f32> {
return vec3<f32>(p.y, -p.x, p.z);
}
//let d = sdfPrimitive3d(op90RotateZ(p));
fn opRotateX(p: vec3<f32>, a: f32) -> vec3<f32> {
let s = sin(a); let c = cos(a);
return vec3<f32>(p.x, c * p.y + s * p.z, -s * p.y + c * p.z);
}
fn opRotateY(p: vec3<f32>, a: f32) -> vec3<f32> {
let s = sin(a); let c = cos(a);
return vec3<f32>(c * p.x - s * p.z, p.y, s * p.x + c * p.z);
}
fn opRotateZ(p: vec3<f32>, a: f32) -> vec3<f32> {
let s = sin(a); let c = cos(a);
return vec3<f32>(c * p.x + s * p.y, -s * p.x + c * p.y, p.z);
}
//let d = sdfPrimitive3d(opRotateY(p, a));
fn opRotateE(p: vec3<f32>, e: vec3<f32>, a: f32) -> vec3<f32> {
let c = cos(a);
return dot(e, p) * (1. - c) * e - cross(e, p) * sin(a) + c * p;
}
//let d = sdfPrimitive3d(opRotateE(p, normalize(vec3<f32>(1.,0.,.5)), a));
fn opScale(p: vec3<f32>, s: f32) -> vec3<f32> {
return p / s;
}
//let d = sdfPrimitive3d(opScale(p, s)) * s;
fn opTransform(p: vec3<f32>, transform: mat4x4<f32>) -> vec3<f32> {
let q = inverse(transform) * vec4<f32>(p, 1.);
}
//let d = sdfPrimitive3d(opTransform(p, transform)) * determinant(transform);
// OR
//let d = sdfPrimitive3d(opScale(opRotateE(opTranslate(p, t), e, a), s)) * s;
fn opSymmetryX(p: vec3<f32>) -> vec3<f32> { return vec3<f32>(abs(p.x), p.y, p.z); }
fn opSymmetryY(p: vec3<f32>) -> vec3<f32> { return vec3<f32>(p.x, abs(p.y), p.z); }
fn opSymmetryZ(p: vec3<f32>) -> vec3<f32> { return vec3<f32>(p.x, p.y, abs(p.z)); }
//let d = sdfPrimitive3d(opSymmetryX(p));
fn opInfArray(p: vec3<f32>, c: vec3<f32>) -> vec3<f32> {
return p - c * round(p / c);
}
//let d = sdfPrimitive3d(opInfArray(p, c));
fn opLimArray(p: vec3<f32>, c: f32, lim: vec3<f32>) -> vec3<f32> {
return p - c * clamp(round(p / c), -lim, lim);
}
//let d = sdfPrimitive3d(opLimArray(p, c));
fn opElongate(p: vec3<f32>, h: vec3<f32>) -> vec3<f32> {
return p - clamp(p, -h, h);
}
//let d = sdfPrimitive3d(opElongateFast(p, h));
fn opElongateCorrect(p: vec3<f32>, h: vec3<f32>) -> vec4<f32> {
let q = abs(p) - h;
let sgn = 2. * step(vec3<f32>(0.), p) - vec3<f32>(1.);
return vec4<f32>(sgn * max(q, vec3<f32>(0.)), min(max(q.x, max(q.y, q.z)), 0.));
}
//let p2 = opElongateCorrect(p, h);
//let d = p2.w + sdfPrimitive3d(p2.xyz);
fn opRound(p: vec3<f32>, r: f32) -> f32 {
return sdfPrimitive3d(p) - r;
}
fn opOnion(d: f32, thickness: f32) -> f32 {
return abs(d) - thickness;
}
//let d = opOnion(sdfPrimitive3d(p), thickness);
fn opExtrusion(d: f32, z: f32, h: f32) -> f32 {
let w = vec2<f32>(d, abs(z) - h);
return min(max(w.x, w.y), 0.) + length(max(w, vec2<f32>(0.)));
}
//let d = opExtrusion(sdfPrimitive2d(p.xy), p.z, h));
fn opRevolution(p: vec3<f32>, o: f32) -> vec2<f32> {
return vec2<f32>(length(p.xz) - o, p.y);
}
//let d = sdfPrimitive2d(opRevolution(p, h));
fn length4(p: vec3<f32>) -> f32 {
var q: vec3<f32> = p * p;
q = q * q;
return sqrt(sqrt(q.x + q.y + q.z));
}
fn length6(p: vec3<f32>) -> f32 {
var q: vec3<f32> = p * p * p;
q = q * q;
return pow(q.x + q.y + q.z, 1. / 6.);
}
fn length8(p: vec3<f32>) -> f32 {
var q: vec3<f32> = 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, Munrocket