// Normal encoding/decoding utilities. // Octahedral encoding: losslessly maps unit normals ↔ vec2f in [-1,1]². // Storage convention: remap to [0,1] before writing to u8/f16 textures. // Encode a unit normal to octahedral XY in [-1, 1]. fn oct_encode(n: vec3f) -> vec2f { let inv_l1 = 1.0 / (abs(n.x) + abs(n.y) + abs(n.z)); var p = n.xy * inv_l1; if (n.z < 0.0) { let s = vec2f(select(-1.0, 1.0, p.x >= 0.0), select(-1.0, 1.0, p.y >= 0.0)); p = (1.0 - abs(p.yx)) * s; } return p; // [-1, 1] } // Decode octahedral XY in [-1, 1] back to a unit normal. fn oct_decode(f: vec2f) -> vec3f { var n = vec3f(f.x, f.y, 1.0 - abs(f.x) - abs(f.y)); let t = max(-n.z, 0.0); n.x += select(t, -t, n.x >= 0.0); n.y += select(t, -t, n.y >= 0.0); return normalize(n); } // Convenience: encode + remap to [0, 1] for texture storage. fn oct_encode_unorm(n: vec3f) -> vec2f { return oct_encode(n) * 0.5 + vec2f(0.5); } // Convenience: undo [0, 1] remap then decode. fn oct_decode_unorm(rg: vec2f) -> vec3f { return oct_decode(rg * 2.0 - vec2f(1.0)); }