indicatrix 0.9.0

A physically-based spectral gemstone renderer that turns GemCAD-style cutting instructions into rendered output.
Documentation
// ---------------------------------------------------------------------------------
// Event Wavefront WGSL Common Definitions and Bindings.
//
// Tags, packed buffer structs, bindings (40..43), load/store helpers,
// and workgroup-shared plane cache cooperative fill.
// ---------------------------------------------------------------------------------

const TAG_DONE: u32 = 0u;
const TAG_EXTEND: u32 = 1u;
const TAG_SCATTER: u32 = 2u;
const TAG_SURFACE: u32 = 3u;
const TAG_ARM_BASE: u32 = 3u;
const TAG_SPLIT: u32 = 9u;
const TAG_TAIL: u32 = 10u;
const TAG_FINALIZE: u32 = 11u;
const TAG_NEE_HG: u32 = 12u;     // scatter event parked for ew_nee_hg (HDR environments only)

const RAY_FLAG_INSIDE_GEM: u32 = 1u;
const RAY_FLAG_IS_EXTRAORDINARY: u32 = 2u;
const RAY_FLAG_HAVE_PREV_PLANE_NORMAL: u32 = 4u;
const RAY_FLAG_PATH_ESCAPED: u32 = 8u;

struct EwParams {            // 32 bytes
    chunk_rays: u32,
    bounce: u32,             // round index; equals the megakernel's loop variable
    finalize_all: u32,       // 1 only on the final ew_finalize dispatch
    has_scatter: u32,
    pad0: u32, pad1: u32, pad2: u32, pad3: u32,
}

struct EwRayCore {           // 128 bytes
    origin: vec4<f32>,               // w unused (0)
    dir: vec4<f32>,                  // w unused (0)
    k: vec4<f32>,                    // w unused (0)
    prev_plane_normal: vec4<f32>,    // w unused (0)
    pending_mis_dir: vec4<f32>,      // w = pending_light_mis
    nee_xyz: vec4<f32>,              // w unused (0)
    observer: vec4<f32>,             // w unused (0)
    ray_meta: vec4<u32>,             // x flags, y seed0, z event tag, w tuple index (pixel*spp+sample)
}

struct EwRayChan {           // 288 bytes
    stokes: array<vec4<f32>, 8>,
    radiance: array<f32, 8>,
    path_pdf: array<f32, 8>,
    split_radiance: array<f32, 8>,
    lambdas: array<f32, 8>,
    compat: array<u32, 8>,
}

struct EwHit {               // 240 bytes
    hit_point: vec4<f32>,            // w = hit.t
    hit_normal: vec4<f32>,           // w unused (0)
    shading_normal: vec4<f32>,
    wave_dir_at_bounce: vec4<f32>,
    phase_dir_this_check: vec4<f32>, // w = phase_pdf_this_check
    misc: vec4<u32>,                 // x hit.hit (0/1), y pre_bounce_inside_gem, z finish, w arm
    split: vec4<u32>,                // x split_mask, y bitcast<u32>(split_mis_weight), z hit.facet_idx, w 0
    split_dir: array<vec4<f32>, 8>,
}

@group(0) @binding(40) var<uniform> ew: EwParams;
@group(0) @binding(41) var<storage, read_write> ray_core: array<EwRayCore>;
@group(0) @binding(42) var<storage, read_write> ray_chan: array<EwRayChan>;
@group(0) @binding(43) var<storage, read_write> ray_hit: array<EwHit>;

fn ew_load(i: u32) -> BounceRay {
    var s: BounceRay;
    let core = ray_core[i];
    s.origin = core.origin.xyz;
    s.dir = core.dir.xyz;
    s.k = core.k.xyz;
    s.prev_plane_normal = core.prev_plane_normal.xyz;
    s.pending_light_mis_dir = core.pending_mis_dir.xyz;
    s.pending_light_mis = core.pending_mis_dir.w;
    s.nee_xyz = core.nee_xyz.xyz;
    let flags = core.ray_meta.x;
    s.inside_gem = (flags & RAY_FLAG_INSIDE_GEM) != 0u;
    s.is_extraordinary = (flags & RAY_FLAG_IS_EXTRAORDINARY) != 0u;
    s.have_prev_plane_normal = (flags & RAY_FLAG_HAVE_PREV_PLANE_NORMAL) != 0u;
    s.path_escaped = (flags & RAY_FLAG_PATH_ESCAPED) != 0u;

    let chan = ray_chan[i];
    for (var k: u32 = 0u; k < NUM_CHANNELS; k = k + 1u) {
        s.lambdas[k] = chan.lambdas[k];
        s.stokes[k] = chan.stokes[k];
        s.radiance[k] = chan.radiance[k];
        s.path_pdf[k] = chan.path_pdf[k];
        s.split_radiance[k] = chan.split_radiance[k];
        s.compat[k] = chan.compat[k];
    }
    return s;
}

fn ew_store(i: u32, s: BounceRay) {
    var flags: u32 = 0u;
    if (s.inside_gem) { flags = flags | RAY_FLAG_INSIDE_GEM; }
    if (s.is_extraordinary) { flags = flags | RAY_FLAG_IS_EXTRAORDINARY; }
    if (s.have_prev_plane_normal) { flags = flags | RAY_FLAG_HAVE_PREV_PLANE_NORMAL; }
    if (s.path_escaped) { flags = flags | RAY_FLAG_PATH_ESCAPED; }

    ray_core[i].origin = vec4<f32>(s.origin, 0.0);
    ray_core[i].dir = vec4<f32>(s.dir, 0.0);
    ray_core[i].k = vec4<f32>(s.k, 0.0);
    ray_core[i].prev_plane_normal = vec4<f32>(s.prev_plane_normal, 0.0);
    ray_core[i].pending_mis_dir = vec4<f32>(s.pending_light_mis_dir, s.pending_light_mis);
    ray_core[i].nee_xyz = vec4<f32>(s.nee_xyz, 0.0);
    ray_core[i].ray_meta.x = flags;

    for (var k: u32 = 0u; k < NUM_CHANNELS; k = k + 1u) {
        ray_chan[i].lambdas[k] = s.lambdas[k];
        ray_chan[i].stokes[k] = s.stokes[k];
        ray_chan[i].radiance[k] = s.radiance[k];
        ray_chan[i].path_pdf[k] = s.path_pdf[k];
        ray_chan[i].split_radiance[k] = s.split_radiance[k];
        ray_chan[i].compat[k] = s.compat[k];
    }
}

fn ew_hit_load(i: u32) -> BounceHit {
    var h = bounce_hit_blank();
    let raw = ray_hit[i];
    h.hit.hit = (raw.misc.x != 0u);
    h.hit.t = raw.hit_point.w;
    h.hit.normal = raw.hit_normal.xyz;
    h.hit.facet_idx = raw.split.z;

    h.hit_point = raw.hit_point.xyz;
    h.shading_normal = raw.shading_normal.xyz;
    h.wave_dir_at_bounce = raw.wave_dir_at_bounce.xyz;
    h.phase_dir_this_check = raw.phase_dir_this_check.xyz;
    h.phase_pdf_this_check = raw.phase_dir_this_check.w;

    h.pre_bounce_inside_gem = (raw.misc.y != 0u);
    h.finish = raw.misc.z;
    h.arm = raw.misc.w;

    h.split_mask = raw.split.x;
    h.split_mis_weight = bitcast<f32>(raw.split.y);
    for (var k: u32 = 0u; k < NUM_CHANNELS; k = k + 1u) {
        h.split_dir[k] = raw.split_dir[k];
    }
    return h;
}

fn ew_hit_store(i: u32, h: BounceHit) {
    ray_hit[i].hit_point = vec4<f32>(h.hit_point, h.hit.t);
    ray_hit[i].hit_normal = vec4<f32>(h.hit.normal, 0.0);
    ray_hit[i].shading_normal = vec4<f32>(h.shading_normal, 0.0);
    ray_hit[i].wave_dir_at_bounce = vec4<f32>(h.wave_dir_at_bounce, 0.0);
    ray_hit[i].phase_dir_this_check = vec4<f32>(h.phase_dir_this_check, h.phase_pdf_this_check);
    ray_hit[i].misc = vec4<u32>(
        select(0u, 1u, h.hit.hit),
        select(0u, 1u, h.pre_bounce_inside_gem),
        h.finish,
        h.arm,
    );
    ray_hit[i].split = vec4<u32>(
        h.split_mask,
        bitcast<u32>(h.split_mis_weight),
        h.hit.facet_idx,
        0u,
    );
    for (var k: u32 = 0u; k < NUM_CHANNELS; k = k + 1u) {
        ray_hit[i].split_dir[k] = h.split_dir[k];
    }
}

fn ew_fill_planes_shared(local_index: u32) {
    // The real planes only: with concave tools the `planes` binding also holds
    // `params.num_tools` packed tool records after them (the host packs them with
    // `geometry::tool::pack_planes_and_tools`), which `scene_plane_count` leaves out. The
    // tool records are read straight from storage by `scene_tool`, never via this cache.
    let plane_count = scene_plane_count();
    if (plane_count <= PLANES_SHARED_CAPACITY) {
        var fill_i = local_index;
        loop {
            if (fill_i >= plane_count) {
                break;
            }
            planes_shared[fill_i] = planes[fill_i];
            fill_i = fill_i + 64u;
        }
    }
    workgroupBarrier();
}