Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

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 pass that fills the G-buffer holds 128 bits of colour a pixel, the storage a tile-based GPU keeps on chip at its full tile size: the shaded colour takes 64, the normal 32, and this channel the 32 left, so 32 bits is the entire budget:

LaneContent
.rbase colour, RGB565 low byte
.gbase colour, RGB565 high byte
.bmetallic
.aperceptual 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.

#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:

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

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.