| name | webgpu-impl-render-usecases |
| description | Use when building WebGPU render workloads: PBR materials, full-screen passes, post-processing, or screen-space effects. Prevents the unnecessary-vertex-buffer mistake for full-screen passes and WebGL clip-space assumptions. Covers PBR material setup, the full-screen oversized-triangle trick, post-processing, and screen-space effects like SSAO and SSR. Keywords: render use case, PBR, physically based rendering, full-screen quad, full-screen triangle, post-processing, screen-space effects, SSAO, SSR, how do I render PBR materials, full screen pass.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires WebGPU 1.0-stable. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
WebGPU Render Use Cases
API-level setup for common render workloads: PBR materials, full-screen passes,
post-processing, and screen-space effects on WebGPU 1.0-stable (Chrome 113+,
Safari 26+, Firefox 141+).
Quick Reference
| Use case | API resources needed | Key technique |
|---|
| PBR material | 2 uniform buffers (matrices, material params), 3 texture_2d<f32>, 1 filtering sampler, 1 bind group | Cook-Torrance BRDF in WGSL fragment shader |
| Full-screen pass | NO vertex buffer, 1 sampled texture_2d<f32>, 1 sampler | Generate 3 vertices from @builtin(vertex_index) |
| Post-processing | Offscreen color texture, full-screen pass per effect | Sample previous result, write next (cross-link webgpu-impl-multipass) |
| Screen-space effects (SSAO, SSR) | G-buffer textures, depth as texture_depth_2d, inverse projection matrix | Full-screen pass reconstructs position from depth |
ALWAYS generate a full-screen pass from three shader-built vertices. NEVER bind a
vertex buffer for a full-screen pass. WebGPU clip-space Z is 0 to 1 (not -1 to
1 like WebGL); the framebuffer is Y-down while clip space is Y-up.
Decision Tree
What render workload are you building?
├── Surface shading with realistic light response
│ -> PBR material. One uniform buffer for camera/transform matrices, one for
│ material parameters (albedo, metallic, roughness). Bind albedo, normal, and
│ metallic-roughness textures plus a filtering sampler. Implement the
│ Cook-Torrance BRDF in the WGSL fragment shader.
│
├── Apply one effect across the whole viewport (copy, tone-map, color grade)
│ -> Full-screen pass. Generate an oversized triangle from
│ @builtin(vertex_index); bind NO vertex buffer. The fragment shader samples
│ the input texture.
│
├── Chain several effects on a finished image (bloom, FXAA, tone-map)
│ -> Post-processing chain. Render the scene to an offscreen texture, then run
│ one full-screen pass per effect. See webgpu-impl-multipass for the pass
│ mechanics and ping-pong buffers.
│
└── Depth/normal-driven effects (SSAO, screen-space reflections)
-> Render scene depth and normals to a G-buffer (see webgpu-impl-multipass),
then a full-screen pass samples those textures. Bind the depth texture as
texture_depth_2d and reconstruct view-space position from depth and the
inverse projection matrix.
Core Patterns
Pattern 1: Full-screen pass uses NO vertex buffer
ALWAYS generate a full-screen pass from three vertices in the vertex shader, indexed
by @builtin(vertex_index). NEVER bind a vertex buffer or define a vertex buffer
layout for a full-screen pass. The oversized triangle covers the whole viewport with
one primitive and no internal seam.
@vertex
fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
// Indices 0,1,2 -> clip-space corners of a triangle 2x the viewport.
let x = f32((i << 1u) & 2u); // 0, 2, 0
let y = f32(i & 2u); // 0, 0, 2
return vec4f(x * 2.0 - 1.0, y * 2.0 - 1.0, 0.0, 1.0);
}
Draw it with pass.draw(3). The render pipeline omits the vertex.buffers field.
Pattern 2: Y-flip for UVs derived from clip-space position
The framebuffer is Y-down while clip space is Y-up. ALWAYS flip the Y component when
deriving texture UVs from the clip-space position so sampled images are not upside
down.
// pos.xy is clip-space [-1,1]; uv must be framebuffer-space [0,1].
let uv = vec2f(pos.x * 0.5 + 0.5, 0.5 - pos.y * 0.5);
Pattern 3: PBR material uses a fixed bind-group split
ALWAYS split PBR resources into one uniform buffer for camera/transform matrices and
one uniform buffer for material parameters (albedo factor, metallic, roughness). Bind
the albedo, normal, and metallic-roughness textures as texture_2d<f32> plus one
filtering sampler. No API feature beyond sampler and texture_2d<f32> entries is
needed. The Cook-Torrance BRDF lives entirely in the WGSL fragment shader.
const layout = device.createBindGroupLayout({
label: "pbr-bgl",
entries: [
{ binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
buffer: { type: "uniform" } },
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: {} },
{ binding: 3, visibility: GPUShaderStage.FRAGMENT, texture: {} },
{ binding: 4, visibility: GPUShaderStage.FRAGMENT, texture: {} },
{ binding: 5, visibility: GPUShaderStage.FRAGMENT, sampler: {} },
],
});
Pattern 4: Correct color space per PBR texture
ALWAYS create the albedo texture with an sRGB format (rgba8unorm-srgb) so the GPU
decodes it to linear on sample. ALWAYS create the normal map and metallic-roughness
texture with a linear format (rgba8unorm). Those textures store raw data, not
perceptual color; an sRGB format would apply a wrong gamma curve to them.
Pattern 5: Screen-space effects sample depth via texture_depth_2d
ALWAYS bind the depth buffer for SSAO or SSR as texture_depth_2d in WGSL and pass
the inverse projection matrix in a uniform buffer. Reconstruct view-space position
from the sampled non-linear depth and the inverse projection matrix; do not assume
depth is a linear distance.
@group(0) @binding(0) var depth_tex: texture_depth_2d;
// Sample with textureLoad (integer coords) or a non-filtering sampler.
let raw_depth = textureLoad(depth_tex, vec2i(frag_coord.xy), 0);
let ndc = vec4f(uv * 2.0 - 1.0, raw_depth, 1.0);
let view_pos_h = inv_proj * ndc;
let view_pos = view_pos_h.xyz / view_pos_h.w;
Pattern 6: Use a [0,1]-Z projection matrix
ALWAYS build the projection matrix for WebGPU's 0 to 1 clip-space Z range. NEVER
reuse a WebGL [-1,1]-Z projection matrix. A WebGL matrix maps the near plane to
-1, so half the depth range falls outside [0,1] and is clipped, breaking depth
testing. See webgpu-impl-webgl-migration for the matrix conversion.
Common Anti-Patterns
-
Full-screen quad from a 4-vertex triangle-strip vertex buffer. WHY it fails:
it adds an unnecessary vertex buffer and layout, and the two triangles meet on a
diagonal seam that can cause sampling artefacts. The shader-generated oversized
triangle is strictly better. Fix: generate 3 vertices from @builtin(vertex_index)
and draw(3).
-
Reusing a WebGL [-1,1]-Z projection matrix. WHY it fails: WebGPU clip-space Z
is [0,1]; a WebGL matrix clips the near half of the scene and corrupts depth
testing. Fix: build the projection for a [0,1] Z range.
-
Marking the metallic-roughness or normal texture as sRGB. WHY it fails: those
textures store linear data (roughness, metalness, packed normals); an sRGB format
applies a gamma curve on sample and produces wrong lighting. Fix: use rgba8unorm
(linear) for normal and metallic-roughness, rgba8unorm-srgb only for albedo.
Critical Warnings
- NEVER bind a vertex buffer for a full-screen pass; generate 3 vertices in the shader.
- NEVER reuse a WebGL
[-1,1]-Z projection matrix; WebGPU clip-space Z is [0,1].
- NEVER sample a depth texture with a filtering sampler; depth needs
textureLoad,
a non-filtering sampler, or a comparison sampler.
- NEVER create the normal or metallic-roughness texture with an sRGB format.
- NEVER sample a texture in the same render pass that still has it bound as an
attachment; split the passes (see
webgpu-impl-multipass).
Reference Files
references/methods.md : per-use-case recipes for PBR materials, full-screen
passes, and screen-space effects.
references/examples.md : verified code for an oversized-triangle pass, a PBR
bind-group layout, and an SSAO-style depth-sampling pass.
references/anti-patterns.md : render-workload mistakes with WHY-it-fails analysis.
Related skills: webgpu-impl-multipass (pass chaining, G-buffers, ping-pong),
webgpu-syntax-render-pipeline (pipeline descriptors), webgpu-wgsl-fragment-shaders
(fragment-stage WGSL), webgpu-syntax-bind-groups (bind group layouts),
webgpu-impl-webgl-migration (WebGL-to-WebGPU porting).