kiss3d 0.45.0

Keep it simple, stupid, 2D and 3D graphics engine for Rust.
Documentation
import package::common::unpack_mat3;
// One level of 2D Radiance Cascades (decoupled layout).
//
// Each cascade is a texture packing a grid of probes; each probe owns an `e x e` tile
// of texels, one per ray direction. Probe spacing `s` and direction-tile edge `e`
// scale independently per level (s doubles, e doubles → direction count quadruples),
// so the cascade texture stays the same size while spacing and angular resolution are
// chosen independently — letting us keep a fine probe grid AND many directions. A
// texel marches its probe's ray over a radial interval against the emitter/occluder
// discs, then merges the higher cascade's continuation (the 4 finer-angle directions,
// bilinearly interpolated across the 4 nearest upper probes at this probe's position).

const MAX_EMITTERS: u32 = 32u;
const MAX_OCCLUDERS: u32 = 64u;
const TAU: f32 = 6.2831853;

struct Emitter {
    pos_radius: vec4<f32>,
    color: vec4<f32>,
}

struct Occluder {
    pos_radius: vec4<f32>,
}

// Reused from gi2d_field.wgsl (the same field uniform buffer is bound).
struct FieldUniforms {
    inv_vp_0: vec4<f32>,
    inv_vp_1: vec4<f32>,
    inv_vp_2: vec4<f32>,
    prev_vp_0: vec4<f32>,
    prev_vp_1: vec4<f32>,
    prev_vp_2: vec4<f32>,
    cur_vp_0: vec4<f32>,
    cur_vp_1: vec4<f32>,
    cur_vp_2: vec4<f32>,
    params: vec4<f32>,
    flags: vec4<f32>,
    counts: vec4<f32>,
    emitters: array<Emitter, MAX_EMITTERS>,
    occluders: array<Occluder, MAX_OCCLUDERS>,
}

struct CascadeParams {
    // dir-tile edge e_c, probe spacing s_c (field px), fieldW, fieldH
    v0: vec4<f32>,
    // upper dir-tile edge e_up, upper spacing s_up, up_probesX, up_probesY
    v1: vec4<f32>,
    // start_c, end_c, max_steps, is_top
    v2: vec4<f32>,
}

@group(0) @binding(0)
var t_upper: texture_2d<f32>;

@group(1) @binding(0)
var<uniform> p: CascadeParams;

@group(2) @binding(0)
var<uniform> field: FieldUniforms;

@group(3) @binding(0)
var t_sdf: texture_2d<f32>;
@group(3) @binding(1)
var s_sdf: sampler;

struct VertexInput {
    @location(0) position: vec2<f32>,
}

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
}

@vertex
fn vs_main(vertex: VertexInput) -> VertexOutput {
    var out: VertexOutput;
    out.clip_position = vec4<f32>(vertex.position, 0.0, 1.0);
    return out;
}

fn unproject(uv: vec2<f32>) -> vec2<f32> {
    let clip = vec2<f32>(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0);
    let inv = mat3x3<f32>(field.inv_vp_0.xyz, field.inv_vp_1.xyz, field.inv_vp_2.xyz);
    let h = inv * vec3<f32>(clip, 1.0);
    return h.xy / h.z;
}

// Distance to the nearest occluder at world point `q`: a jump-flooded SDF texture
// fetch when `use_sdf`, otherwise the analytic minimum over the occluder discs.
fn occluder_distance(q: vec2<f32>) -> f32 {
    if (field.flags.x > 0.5) {
        let clip = mat3x3<f32>(field.cur_vp_0.xyz, field.cur_vp_1.xyz, field.cur_vp_2.xyz)
            * vec3<f32>(q, 1.0);
        let uv = vec2<f32>((clip.x + 1.0) * 0.5, (1.0 - clip.y) * 0.5);
        if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
            return 1e9;
        }
        // Bias the unsigned field so it crosses zero just outside the surface.
        return textureSampleLevel(t_sdf, s_sdf, uv, 0.0).r - field.flags.y;
    }
    let num_o = u32(field.counts.y);
    var d = 1e9;
    for (var i = 0u; i < num_o; i = i + 1u) {
        let o = field.occluders[i];
        d = min(d, length(q - o.pos_radius.xy) - o.pos_radius.z);
    }
    return d;
}

// March a ray over [start, end): returns (radiance.rgb, transmittance).
fn march_interval(origin: vec2<f32>, dir: vec2<f32>, start: f32, end: f32, max_steps: u32) -> vec4<f32> {
    let num_e = u32(field.counts.x);

    var t = start;
    for (var step = 0u; step < max_steps; step = step + 1u) {
        let q = origin + dir * t;

        var d_emit = 1e9;
        var ei = 0u;
        for (var i = 0u; i < num_e; i = i + 1u) {
            let e = field.emitters[i];
            let d = length(q - e.pos_radius.xy) - e.pos_radius.z;
            if (d < d_emit) {
                d_emit = d;
                ei = i;
            }
        }
        let d_occ = occluder_distance(q);

        if (d_emit <= 0.0) {
            let e = field.emitters[ei];
            return vec4<f32>(e.color.rgb * e.pos_radius.w, 0.0);
        }
        if (d_occ <= 0.0) {
            return vec4<f32>(0.0, 0.0, 0.0, 0.0);
        }

        t += max(min(d_emit, d_occ), 0.5);
        if (t >= end) {
            return vec4<f32>(0.0, 0.0, 0.0, 1.0);
        }
    }
    return vec4<f32>(0.0, 0.0, 0.0, 1.0);
}

fn read_upper(probe: vec2<i32>, dir: i32, e: i32, probes: vec2<i32>) -> vec4<f32> {
    let pc = clamp(probe, vec2<i32>(0), probes - vec2<i32>(1));
    let coord = pc * e + vec2<i32>(dir % e, dir / e);
    return textureLoad(t_upper, coord, 0);
}

@fragment
fn fs_main(@builtin(position) frag: vec4<f32>) -> @location(0) vec4<f32> {
    let coord = vec2<i32>(frag.xy);
    let e_c = i32(p.v0.x);
    let s_c = p.v0.y;
    let field_size = p.v0.zw;

    let probe = coord / e_c;
    let within = coord - probe * e_c;
    let dir = within.y * e_c + within.x;
    let dir_count = e_c * e_c;

    let probe_px = (vec2<f32>(probe) + 0.5) * s_c;
    let origin = unproject(probe_px / field_size);
    let angle = TAU * (f32(dir) + 0.5) / f32(dir_count);
    let rdir = vec2<f32>(cos(angle), sin(angle));

    let near = march_interval(origin, rdir, p.v2.x, p.v2.y, u32(p.v2.z));

    // Top cascade: nothing above to merge.
    if (p.v2.w > 0.5 || near.a <= 0.0) {
        return near;
    }

    let e_up = i32(p.v1.x);
    let s_up = p.v1.y;
    let up_probes = vec2<i32>(i32(p.v1.z), i32(p.v1.w));
    // This probe's position in the upper probe grid (both in field pixels).
    let pf = probe_px / s_up - vec2<f32>(0.5);
    let p0 = vec2<i32>(floor(pf));
    let fr = pf - floor(pf);
    let w00 = (1.0 - fr.x) * (1.0 - fr.y);
    let w10 = fr.x * (1.0 - fr.y);
    let w01 = (1.0 - fr.x) * fr.y;
    let w11 = fr.x * fr.y;

    var cont = vec4<f32>(0.0);
    for (var k = 0; k < 4; k = k + 1) {
        let ud = dir * 4 + k;
        cont += w00 * read_upper(p0 + vec2<i32>(0, 0), ud, e_up, up_probes);
        cont += w10 * read_upper(p0 + vec2<i32>(1, 0), ud, e_up, up_probes);
        cont += w01 * read_upper(p0 + vec2<i32>(0, 1), ud, e_up, up_probes);
        cont += w11 * read_upper(p0 + vec2<i32>(1, 1), ud, e_up, up_probes);
    }
    cont *= 0.25;

    return vec4<f32>(near.rgb + near.a * cont.rgb, near.a * cont.a);
}