viewport-lib 0.18.3

3D viewport rendering library
Documentation
// Diffuse irradiance convolution of an equirectangular HDR skybox.
//
// One invocation per destination texel. Each invocation walks the upper
// hemisphere with cos-weighted hemisphere sampling, reading the source
// skybox by 2D equirect projection.

const PI: f32 = 3.14159265358979;

@group(0) @binding(0) var src_tex: texture_2d<f32>;
@group(0) @binding(1) var src_sampler: sampler;
@group(0) @binding(2) var dst_tex: texture_storage_2d<rgba16float, write>;

// viewport-lib is Z-up: longitude around +Z, latitude with +Z polar.
fn dir_to_equirect_uv(dir: vec3<f32>) -> vec2<f32> {
    let phi = atan2(dir.y, dir.x);
    let theta = asin(clamp(dir.z, -1.0, 1.0));
    return vec2<f32>(0.5 + phi / (2.0 * PI), 0.5 - theta / PI);
}

@compute @workgroup_size(8, 8)
fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
    let dims = textureDimensions(dst_tex);
    if (gid.x >= dims.x || gid.y >= dims.y) {
        return;
    }

    let u = f32(gid.x) / f32(dims.x);
    let v = f32(gid.y) / f32(dims.y);
    let theta_n = PI * (0.5 - v);
    let phi_n = 2.0 * PI * (u - 0.5);

    let st = sin(theta_n);
    let ct = cos(theta_n);
    let sp = sin(phi_n);
    let cp = cos(phi_n);
    // Z-up: latitude theta drives Z, longitude phi spins around Z in the XY plane.
    let normal = vec3<f32>(ct * cp, ct * sp, st);

    // Pick a non-collinear reference axis to build the tangent frame.
    var up: vec3<f32>;
    if (abs(normal.z) < 0.999) {
        up = vec3<f32>(0.0, 0.0, 1.0);
    } else {
        up = vec3<f32>(1.0, 0.0, 0.0);
    }
    let tangent = normalize(cross(up, normal));
    let bitangent = cross(normal, tangent);

    var irr = vec3<f32>(0.0);
    var sample_count: f32 = 0.0;
    let sample_delta: f32 = 0.05;

    var s_phi: f32 = 0.0;
    loop {
        if (s_phi >= 2.0 * PI) { break; }
        var s_theta: f32 = 0.0;
        loop {
            if (s_theta >= 0.5 * PI) { break; }
            let sst = sin(s_theta);
            let sct = cos(s_theta);
            let ssp = sin(s_phi);
            let scp = cos(s_phi);
            let ts = vec3<f32>(sst * scp, sst * ssp, sct);
            let dir = normalize(ts.x * tangent + ts.y * bitangent + ts.z * normal);
            let uv = dir_to_equirect_uv(dir);
            let c = textureSampleLevel(src_tex, src_sampler, uv, 0.0).rgb;
            let w = sct * sst;
            irr += c * w;
            sample_count += 1.0;
            s_theta += sample_delta;
        }
        s_phi += sample_delta;
    }

    let scale = PI / sample_count;
    textureStore(dst_tex, vec2<i32>(gid.xy), vec4<f32>(irr * scale, 1.0));
}