sdf-morph@0.1.0
SDF morph
The distance field itself, drawn as contour lines: a rounded box and an orbiting disc under a smooth boolean, a smaller disc carving in. The blue isoline is the shape's edge, distance zero.
Controls
#3391ff
Imports
What this entry shader composes; every module resolves from npm.
import { sdCircle, sdRoundedBox2 } from "@vshaders/sdf/2d";
import { opSmoothSubtract, opSmoothUnion } from "@vshaders/sdf/ops";
import { rotate2d } from "@vgpu/wgsl-std/math";
Source
The full entry shader, sdf-morph.wgsl. Copy it and it is yours.
// effect 002 · sdf-morph — the distance field itself, drawn as contour lines.
// A rounded box and an orbiting disc under a smooth boolean, with a smaller
// disc carving into the result. The blue zero isoline is the shape's edge.
// Display-referred on purpose: this is a technical drawing, not a photo.
import { sdCircle, sdRoundedBox2 } from "@vshaders/sdf/2d";
import { opSmoothSubtract, opSmoothUnion } from "@vshaders/sdf/ops";
import { rotate2d } from "@vgpu/wgsl-std/math";
// Tunable members follow resolution/time. Defaults live in lib/effects.ts and
// must be set by the runner: an unset uniform member reads as zero.
struct Uniforms {
resolution: vec2f,
time: f32,
// Multiplies time in every animation term.
speed: f32,
// Zero-isoline color, display-referred like the rest of this effect.
accentColor: vec3f,
// opSmoothUnion band between box and disc, in field units.
smoothness: f32,
// Distance between contour lines, in field units.
lineSpacing: f32,
}
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@fragment
fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
let res = max(uniforms.resolution, vec2f(1.0));
let uv = position.xy / res;
let aspect = res.x / res.y;
let p = (uv - vec2f(0.5)) * vec2f(aspect, 1.0);
let t = uniforms.time * uniforms.speed;
let box = sdRoundedBox2(rotate2d(p, t * 0.2), vec2f(0.17), 0.05);
let orbit = vec2f(cos(t * 0.5), sin(t * 0.5)) * 0.24;
let disc = sdCircle(p - orbit, 0.11);
let blob = opSmoothUnion(box, disc, uniforms.smoothness);
let cutter = sdCircle(p - vec2f(cos(t * 0.31) * 0.12, sin(t * 0.43) * 0.13), 0.08);
let field = opSmoothSubtract(blob, cutter, 0.06);
// Screen-space-constant line widths via fwidth.
let w = max(fwidth(field), 1e-5);
// The contour fract() divides by the spacing, so clamp it away from zero.
let spacing = max(uniforms.lineSpacing, 1e-4);
let contour = abs(fract(field / spacing + 0.5) - 0.5) * spacing;
let lines = 1.0 - smoothstep(0.5 * w, 1.8 * w, contour);
let zeroLine = 1.0 - smoothstep(w, 3.5 * w, abs(field));
let inside = 1.0 - smoothstep(0.0, w, field);
let ink = vec3f(0.0, 0.0, 0.0);
let faint = vec3f(0.19, 0.20, 0.23);
let fill = vec3f(0.075, 0.08, 0.10);
var color = ink;
color = mix(color, fill, inside);
color = mix(color, faint, lines * mix(0.55, 1.0, inside));
color = mix(color, uniforms.accentColor, zeroLine);
return vec4f(color, 1.0);
}