awsm-renderer 0.1.7

awsm-renderer
Documentation
// --- constants & helpers -------------------------------------
const PI      : f32 = 3.1415926535897932384626433832795;
const TAU     : f32 = 6.283185307179586476925286766559; // 2*PI
const EPSILON : f32 = 1e-4;
const F32_MAX = 2139095039u;
const U32_MAX = 4294967295u;

fn saturate(x: f32) -> f32 { return clamp(x, 0.0, 1.0); }
fn saturate3(v: vec3<f32>) -> vec3<f32> { return clamp(v, vec3<f32>(0.0), vec3<f32>(1.0)); }

// attenuation for a point/spot light, matches Unity/Filament
fn inverse_square(range: f32, dist: f32) -> f32 {
    if (range == 0.0) {        // infinite
        return 1.0 / max(dist * dist, 0.01);
    }
    let denom = dist * dist + 1.0;
    let falloff = (1.0 - (dist * dist) / (range * range));
    return saturate(falloff * falloff) / denom;
}

fn safe_normalize(normal: vec3<f32>) -> vec3<f32> {
    let len_sq = dot(normal, normal);
    if (len_sq > 0.0) {
        return normal * inverseSqrt(len_sq);
    }
    // fallback: up vector to avoid NaNs; scene lighting expects unit normal
    return vec3<f32>(0.0, 0.0, 1.0);
}

fn join32(lo: u32, hi: u32) -> u32 {
  return (hi << 16u) | (lo & 0xFFFFu);
}

fn split16(x: u32) -> vec2<u32> {
  let lo = x & 0xFFFFu;
  let hi = x >> 16u;
  return vec2<u32>(lo, hi);
}

// ------------------------------------------------------------
// Octahedral normal encoding (unit normal <-> vec2)
// Encodes a unit normal into 2 channels with minimal distortion
// ------------------------------------------------------------
fn encode_octahedral(n_in: vec3<f32>) -> vec2<f32> {
    var n = n_in / (abs(n_in.x) + abs(n_in.y) + abs(n_in.z));
    if (n.z < 0.0) {
        let one = vec2<f32>(1.0, 1.0);
        let sgn = sign(n.xy);
        let wrapped = (one - abs(n.yx)) * sgn;
        n = vec3<f32>(wrapped.x, wrapped.y, n.z);
    }
    return n.xy * 0.5 + vec2<f32>(0.5, 0.5);
}

fn decode_octahedral(e: vec2<f32>) -> vec3<f32> {
    let f = e * 2.0 - vec2<f32>(1.0, 1.0);
    var n = vec3<f32>(f.x, f.y, 1.0 - abs(f.x) - abs(f.y));
    let t = clamp(-n.z, 0.0, 1.0);

    // Add -t where n.xy >= 0, else +t (per component)
    let vx = select(t, -t, n.x >= 0.0);
    let vy = select(t, -t, n.y >= 0.0);
    n = vec3<f32>(n.x + vx, n.y + vy, n.z);

    return normalize(n);
}

// ------------------------------------------------------------
// Stable canonical tangent/bitangent basis (Frisvad-style)
// Generates an orthonormal basis from a normal vector
// ------------------------------------------------------------
struct TB { t: vec3<f32>, b: vec3<f32> };

fn canonical_tb(n: vec3<f32>) -> TB {
    if (n.z < -0.9999999) {
        return TB(vec3<f32>(0.0, -1.0, 0.0), vec3<f32>(-1.0,  0.0, 0.0));
    } else {
        let a  = 1.0 / (1.0 + n.z);
        let bb = -n.x * n.y * a;
        let t  = vec3<f32>(1.0 - n.x * n.x * a, bb, -n.x);
        let b  = vec3<f32>(bb, 1.0 - n.y * n.y * a, -n.y);
        return TB(t, b);
    }
}

// ------------------------------------------------------------
// TBN packing/unpacking
// Pack: N (unit), T (unit), s (+1 right-handed, -1 left-handed)
// -> vec4<f32> : [octN.xy, angleU, signU]
// angleU = theta in [0,1] where theta is rotation of T in N's plane
// signU  = 1 for s>0, 0 for s<=0
// ------------------------------------------------------------
fn pack_normal_tangent(N: vec3<f32>, T: vec3<f32>, s: f32) -> vec4<f32> {
    let octN   = encode_octahedral(N);
    let tb     = canonical_tb(N);
    let x      = dot(T, tb.t);
    let y      = dot(T, tb.b);
    let theta  = atan2(y, x);                 // [-PI, PI]
    let angleU = (theta + PI) / TAU;          // [0,1]
    let signU  = select(0.0, 1.0, s > 0.0);   // 0 or 1
    return vec4<f32>(octN.x, octN.y, angleU, signU);
}

// Unpack: vec4<f32> -> N, T, B (orthonormal)
struct TBN { N: vec3<f32>, T: vec3<f32>, B: vec3<f32> };

fn unpack_normal_tangent(rgba: vec4<f32>) -> TBN {
    let N     = decode_octahedral(rgba.xy);
    let theta = rgba.z * TAU - PI;           // [-PI, PI]
    let s     = select(-1.0, 1.0, rgba.w >= 0.5);

    let tb0 = canonical_tb(N);
    let T   = normalize(cos(theta) * tb0.t + sin(theta) * tb0.b);
    let B   = s * normalize(cross(N, T));
    return TBN(N, T, B);
}

// Convert relative indices to absolute indices (0 stays 0)
fn abs_index(base_index: u32, relative_index: u32) -> u32 {
    return select(0u, base_index + relative_index, relative_index != 0u);
}