kiss3d 0.45.0

Keep it simple, stupid, 2D and 3D graphics engine for Rust.
Documentation
import package::common::{fullscreen_triangle_xy, fullscreen_uv_from_clip};
// Depth of field — two fragment entry points sharing one full-screen vertex stage.
//
// `fs_coc` reads the resolved HDR scene + the prepass view-position G-buffer and
// writes `vec4(color.rgb, signed_coc)` into the DoF chain (mip 0). The signed
// circle-of-confusion diameter is in pixels: negative in front of the focal plane
// (near field), positive behind it (far field). A box mip chain is then built from
// that texture (so each coarser mip averages both color and CoC), giving cheap
// pre-blurred reads at large blur radii.
//
// `fs_gather` reconstructs each output pixel by gathering a golden-angle spiral of
// taps over a disk of radius = max CoC. A tap contributes only where its own CoC
// reaches the destination pixel (scatter-as-gather), which keeps blurry foreground
// bleeding over sharp focus while preventing sharp background from bleeding onto
// it. The mip LOD of each tap grows with its distance so a fixed tap budget still
// covers large blur radii smoothly. Two kernels: a uniform disk (Bokeh) and a
// gaussian falloff (Gaussian). The composite is written back over the HDR scene.

const PI: f32 = 3.14159265359;
const GOLDEN_ANGLE: f32 = 2.39996323;

struct DofUniforms {
    proj: mat4x4<f32>,
    // (inv_w, inv_h, viewport_height, max_lod)
    params0: vec4<f32>,
    // (focal_distance, aperture_f_stops, sensor_height, max_coc_diameter)
    params1: vec4<f32>,
    // (max_depth, background_depth, mode, num_taps)
    params2: vec4<f32>,
}

@group(0) @binding(0) var t_a: texture_2d<f32>;
// `t_b` is the view-position G-buffer in the CoC pass and unused in the gather
// pass (the same two-texture layout serves both pipelines).
@group(0) @binding(1) var t_b: texture_2d<f32>;
@group(0) @binding(2) var samp: sampler;
@group(0) @binding(3) var<uniform> u: DofUniforms;

struct VsOut {
    @builtin(position) pos: vec4<f32>,
    @location(0) uv: vec2<f32>,
};

@vertex
fn vs_main(@builtin(vertex_index) vid: u32) -> VsOut {
    let xy = fullscreen_triangle_xy(vid);
    var o: VsOut;
    o.pos = vec4<f32>(xy, 0.0, 1.0);
    o.uv = fullscreen_uv_from_clip(xy);
    return o;
}

// Signed CoC diameter (pixels) for a given linear (positive) view-space depth.
fn circle_of_confusion(depth_in: f32) -> f32 {
    let depth = max(min(depth_in, u.params2.x), 1e-4); // clamp to max_depth
    let focal_distance = u.params1.x;
    let f_stops = max(u.params1.y, 1e-3);
    let sensor_h = max(u.params1.z, 1e-6);
    // proj[1][1] = cot(fov_y / 2); focal_length and aperture share sensor units.
    let focal_length = 0.5 * sensor_h * u.proj[1][1];
    let aperture_d = focal_length / f_stops;
    let denom = focal_distance - focal_length;
    var coc_frac = 0.0;
    if abs(denom) > 1e-6 {
        // Signed CoC as a fraction of the sensor / viewport height.
        coc_frac = aperture_d * focal_length / denom * (1.0 - focal_distance / depth) / sensor_h;
    }
    let coc_px = coc_frac * u.params0.z; // -> pixels (× viewport height)
    let max_coc = u.params1.w;
    return clamp(coc_px, -max_coc, max_coc);
}

@fragment
fn fs_coc(in: VsOut) -> @location(0) vec4<f32> {
    let color = textureSampleLevel(t_a, samp, in.uv, 0.0).rgb;
    let vp = textureSampleLevel(t_b, samp, in.uv, 0.0);
    // Background (no opaque surface) is treated as the far clip plane so the sky
    // gets the same far-field blur a distant surface would.
    var depth = u.params2.y;
    if vp.a >= 0.5 {
        depth = max(-vp.z, 1e-4); // view space looks down -Z
    }
    return vec4<f32>(color, circle_of_confusion(depth));
}

@fragment
fn fs_gather(in: VsOut) -> @location(0) vec4<f32> {
    let uv = in.uv;
    let texel = u.params0.xy;
    let max_lod = u.params0.w;
    let bokeh = u.params2.z < 0.5;
    let num_taps = i32(u.params2.w);

    let center = textureSampleLevel(t_a, samp, uv, 0.0);
    let center_coc = center.a;

    // Gather over the maximum possible blur disk so foreground pixels can scatter
    // onto in-focus neighbours; sparse far taps are smoothed by the mip chain.
    let gather_r = max(u.params1.w * 0.5, 1.0);
    if gather_r < 1.0 {
        return vec4<f32>(center.rgb, 1.0);
    }

    var color = center.rgb;
    var weight = 1.0;
    for (var i = 0; i < num_taps; i = i + 1) {
        let t = (f32(i) + 0.5) / f32(num_taps);
        let r = sqrt(t) * gather_r;            // uniform areal distribution
        let ang = f32(i) * GOLDEN_ANGLE;
        let off = vec2<f32>(cos(ang), sin(ang)) * r;
        let lod = clamp(log2(max(r, 1.0)) - 1.0, 0.0, max_lod);
        let s = textureSampleLevel(t_a, samp, uv + off * texel, lod);
        let s_r = abs(s.a);

        // A tap reaches this pixel only if its CoC radius covers the distance `r`.
        var w = smoothstep(r - 1.0, r + 1.0, s_r);
        // Keep sharp background from leaking onto a focused pixel: a far tap
        // (larger, positive CoC than the centre) is limited to the centre's reach.
        if s.a > center_coc {
            w = w * smoothstep(abs(center_coc) + 1.0, abs(center_coc) - 1.0, r);
        }
        if !bokeh {
            // Gaussian kernel: weight by distance within the tap's own radius.
            let sigma = max(s_r, 1.0) * 0.5;
            w = w * exp(-(r * r) / (2.0 * sigma * sigma));
        }
        color += s.rgb * w;
        weight += w;
    }
    return vec4<f32>(color / weight, 1.0);
}