// G-buffer channel visualization — 4×5 grid of 20 feature channels. // Takes feat_tex0 (rgba32uint, ch 0-7 f16) and feat_tex1 (rgba32uint, ch 8-19 unorm8). // Outputs tiled channel view to a standard rgba8unorm render target. // // Channel layout (row×col): // Row 0: ch0(alb.r) ch1(alb.g) ch2(alb.b) ch3(nrm.x) // Row 1: ch4(nrm.y) ch5(depth) ch6(dzdx) ch7(dzdy) // Row 2: ch8(matid) ch9(prv.r) ch10(prv.g) ch11(prv.b) // Row 3: ch12(m1.r) ch13(m1.g) ch14(m1.b) ch15(m2.r) // Row 4: ch16(m2.g) ch17(m2.b) ch18(shdw) ch19(trns) struct GBufViewUniforms { resolution: vec2f } @group(0) @binding(0) var feat0: texture_2d; @group(0) @binding(1) var feat1: texture_2d; @group(0) @binding(2) var u: GBufViewUniforms; @vertex fn vs_main(@builtin(vertex_index) vid: u32) -> @builtin(position) vec4f { var corners = array( vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0)); return vec4f(corners[vid], 0.0, 1.0); } @fragment fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f { let uv = pos.xy / u.resolution; let COLS = 4.0; let ROWS = 5.0; let col = u32(uv.x * COLS); let row = u32(uv.y * ROWS); let ch = row * 4u + col; if (col >= 4u || ch >= 20u) { return vec4f(0.05, 0.05, 0.05, 1.0); } // 1-pixel grid lines (thin border per cell) let lx = fract(uv.x * COLS); let ly = fract(uv.y * ROWS); if (lx < 0.005 || lx > 0.995 || ly < 0.005 || ly > 0.995) { return vec4f(0.25, 0.25, 0.25, 1.0); } // Map local UV to texel coordinate let dim = vec2i(textureDimensions(feat0)); let tc = clamp(vec2i(vec2f(lx, ly) * vec2f(dim)), vec2i(0), dim - vec2i(1)); var v: f32 = 0.0; if (ch < 8u) { // feat0: 4 × pack2x16float — each u32 component holds two f16 values let t = textureLoad(feat0, tc, 0); let pair_idx = ch >> 1u; let sub = ch & 1u; var p: vec2f; if (pair_idx == 0u) { p = unpack2x16float(t.x); } else if (pair_idx == 1u) { p = unpack2x16float(t.y); } else if (pair_idx == 2u) { p = unpack2x16float(t.z); } else { p = unpack2x16float(t.w); } v = select(p.y, p.x, sub == 0u); } else { // feat1: 3 × pack4x8unorm — components .x/.y/.z hold 4 u8 values each let t = textureLoad(feat1, tc, 0); let ch1 = ch - 8u; let comp_idx = ch1 / 4u; let sub = ch1 % 4u; var bytes: vec4f; if (comp_idx == 0u) { bytes = unpack4x8unorm(t.x); } else if (comp_idx == 1u) { bytes = unpack4x8unorm(t.y); } else { bytes = unpack4x8unorm(t.z); } var ba = array(bytes.x, bytes.y, bytes.z, bytes.w); v = ba[sub]; } // Channel-specific normalization for display clarity var disp: f32; if (ch <= 2u) { // Albedo: already [0,1] disp = clamp(v, 0.0, 1.0); } else if (ch == 3u || ch == 4u) { // Normals oct-encoded in [-1,1] → remap to [0,1] disp = clamp(v * 0.5 + 0.5, 0.0, 1.0); } else if (ch == 5u) { // Depth [0,1]: invert so near=white, far=dark disp = clamp(1.0 - v, 0.0, 1.0); } else if (ch == 6u || ch == 7u) { // Depth gradients (signed, small values): amplify × 20 + 0.5 for visibility disp = clamp(v * 20.0 + 0.5, 0.0, 1.0); } else { // Everything else: clamp to [0,1] disp = clamp(v, 0.0, 1.0); } return vec4f(disp, disp, disp, 1.0); }