# gbuffer_material
The G-buffer's surface description, and the one encoding of it.
`@scene.shaded` carries what a surface sent — the light the geometry pass shaded
it to, scene-referred and unbounded above 1. `@scene.material` carries what the
surface is: the base colour it reflects, how metallic it is, and how rough. A
screen-space pass that multiplies incoming light by a reflectance — a bounce
multiplier, a Fresnel tint, an ambient floor — reads the second.
The channel is `Rgba8Uint`. The G-buffer's five colour attachments spend the
whole 32-byte `maxColorAttachmentBytesPerSample` a conformant device guarantees,
and this attachment's four bytes come out of alignment padding the set
otherwise burns, so 32 bits is the entire budget:
| Lane | Content |
|---|---|
| `.r` | base colour, RGB565 low byte |
| `.g` | base colour, RGB565 high byte |
| `.b` | metallic |
| `.a` | perceptual roughness |
Base colour lands at 5/6/5 — steps of about 3% per channel. Metallic and
roughness keep a full byte each.
The geometry pass packs through `zero_gbuffer_pack_material`; every reader
unpacks through this module, so the layout is stated once.
Pure math, no bindings.
```wgsl
#include "@builtin::shaderModules.gbuffer_material"
// bindings.yaml: - { name: scene_material, kind: texture2d, sample: uint }
let m = zero_gbuffer_material(scene_material, coord);
let bounce = incoming * m.base_color;
```
One value at a time:
```wgsl
let base = zero_gbuffer_base_color(scene_material, coord);
let metallic = zero_gbuffer_metallic(scene_material, coord);
let roughness = zero_gbuffer_roughness(scene_material, coord);
```
A `.computeShader` reading the channel declares its slot `kind: texture2d` with
`sample: uint`, because an `Rgba8Uint` texture binds as `texture_2d<u32>`. The
engine refuses the pass when the declared sample type and the bound texture's
format disagree, and names both.
## Signatures
```wgsl
struct ZeroGbufferMaterial {
base_color: vec3<f32>,
metallic: f32,
perceptual_roughness: f32,
}
fn zero_gbuffer_unpack_material(texel: vec4<u32>) -> ZeroGbufferMaterial
fn zero_gbuffer_material(t: texture_2d<u32>, coord: vec2<i32>) -> ZeroGbufferMaterial
fn zero_gbuffer_base_color(t: texture_2d<u32>, coord: vec2<i32>) -> vec3<f32>
fn zero_gbuffer_metallic(t: texture_2d<u32>, coord: vec2<i32>) -> f32
fn zero_gbuffer_roughness(t: texture_2d<u32>, coord: vec2<i32>) -> f32
```
`texel` is one `textureLoad` result off `@scene.material` — the four raw
`u32` lanes before this module gives them meaning. `t` is the bound
`@scene.material` slot itself (`texture_2d<u32>`, declared `sample: uint`);
`coord` is the pixel to read, an integer texel coordinate (`vec2<i32>`) — the
same `textureLoad`-shaped argument every accessor and `zero_gbuffer_material`
take. The three single-value accessors are `zero_gbuffer_material` narrowed to
one field, for a caller that wants only one of the three.
The channel is a G-buffer channel, so the deferred G-buffer fragment entry is
what writes it: an opaque draw on the deferred path. A pixel no geometry wrote
reads a black surface at metallic 0 and mid roughness.