kiss3d 0.45.0

Keep it simple, stupid, 2D and 3D graphics engine for Rust.
Documentation
import package::common::luminance;
// Edge-aware à-trous wavelet denoiser for the path tracer.
//
// One iteration of an SVGF-style à-trous filter: a 5x5 B-spline kernel is
// applied at an exponentially growing tap spacing (`step`), with edge-stopping
// weights derived from the guide normal and from the luminance of the radiance
// itself. The driver runs this entry point several times with increasing
// `step` values, ping-ponging between two scratch buffers.
//
// Albedo demodulation: before the very first iteration the driver divides the
// radiance by (albedo + eps) so only the incident lighting is filtered (the
// `demodulate` flag computes this on the fly when reading from the raw
// accumulation buffer); after the last iteration the result is re-multiplied by
// the albedo on the final iteration via the `remodulate` flag, so `dst` holds
// final HDR radiance and the tonemap pass is left untouched. This keeps crisp
// texture/albedo detail while smoothing the noisy lighting.

struct DenoiseUniforms {
    width: u32,
    height: u32,
    // Tap spacing for this iteration (1, 2, 4, ...).
    step: i32,
    // 1 on the first iteration: `src` is the raw accumulation buffer and must be
    // albedo-demodulated on read.
    demodulate: u32,
    // 1 on the last iteration: re-multiply the filtered lighting by the albedo
    // so `dst` holds final HDR radiance again.
    remodulate: u32,
    // Edge-stopping strength for the normal guide (power exponent).
    sigma_normal: f32,
    // Edge-stopping strength for luminance (larger = more blurring).
    sigma_luminance: f32,
    pad0: f32,
};

@group(0) @binding(0) var<storage, read> src: array<vec4<f32>>;
@group(0) @binding(1) var<storage, read_write> dst: array<vec4<f32>>;
// The shared accumulation buffer (radiance + guides). The first `width*height`
// pixels are radiance, the next are the albedo guide, the next the normal guide.
// `src` aliases its radiance region on the first iteration; on later iterations
// `src` is a scratch buffer, but the guides always come from here.
@group(0) @binding(2) var<storage, read> guides: array<vec4<f32>>;
@group(0) @binding(3) var<uniform> u: DenoiseUniforms;

const EPS: f32 = 1e-3;



// First-hit albedo guide at pixel `idx` (region 1 of the shared buffer).
fn guide_albedo(idx: u32) -> vec3<f32> {
    return guides[u.width * u.height + idx].rgb;
}

// First-hit world-normal guide at pixel `idx` (region 2 of the shared buffer).
fn guide_normal(idx: u32) -> vec3<f32> {
    return guides[2u * u.width * u.height + idx].rgb;
}

// Returns the (demodulated, if requested) lighting at pixel `idx`.
fn read_lighting(idx: u32) -> vec3<f32> {
    let radiance = src[idx].rgb;
    if (u.demodulate != 0u) {
        return radiance / (guide_albedo(idx) + vec3<f32>(EPS));
    }
    return radiance;
}

@compute @workgroup_size(8, 8, 1)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
    if (gid.x >= u.width || gid.y >= u.height) {
        return;
    }
    let cidx = gid.y * u.width + gid.x;

    let center_light = read_lighting(cidx);
    let center_normal = guide_normal(cidx);
    let center_lum = luminance(center_light);

    // 5x5 B-spline (à-trous) kernel weights along one axis: 1/16, 1/4, 3/8.
    let kernel = array<f32, 3>(0.375, 0.25, 0.0625);

    var sum = vec3<f32>(0.0);
    var weight_sum = 0.0;

    for (var dy: i32 = -2; dy <= 2; dy = dy + 1) {
        for (var dx: i32 = -2; dx <= 2; dx = dx + 1) {
            let sx = i32(gid.x) + dx * u.step;
            let sy = i32(gid.y) + dy * u.step;
            if (sx < 0 || sy < 0 || sx >= i32(u.width) || sy >= i32(u.height)) {
                continue;
            }
            let sidx = u32(sy) * u.width + u32(sx);

            let sample_light = read_lighting(sidx);
            let sample_normal = guide_normal(sidx);

            // à-trous spatial kernel (separable weights multiplied).
            let h = kernel[abs(dx)] * kernel[abs(dy)];

            // Edge-stopping on the surface normal: only blur across pixels whose
            // first-hit normal is closely aligned with the center's.
            let n_dot = max(dot(center_normal, sample_normal), 0.0);
            let w_normal = pow(n_dot, u.sigma_normal);

            // Edge-stopping on luminance: preserve high-contrast lighting edges
            // (shadow boundaries, highlights) instead of smearing them.
            let lum_diff = abs(center_lum - luminance(sample_light));
            let w_lum = exp(-lum_diff / (u.sigma_luminance + EPS));

            let w = h * w_normal * w_lum;
            sum = sum + sample_light * w;
            weight_sum = weight_sum + w;
        }
    }

    var filtered = center_light;
    if (weight_sum > 0.0) {
        filtered = sum / weight_sum;
    }

    if (u.remodulate != 0u) {
        // Re-apply the albedo so `dst` is HDR radiance the tonemap pass expects.
        filtered = filtered * (guide_albedo(cidx) + vec3<f32>(EPS));
    }

    dst[cidx] = vec4<f32>(filtered, 1.0);
}