zgpu gems


Status: work in progress. This is a living post — a running collection of small, self-contained graphics notes (“gems”) on doing WebGPU in Zig with zgpu. Entries get filled in as I write them.

I keep hitting the same small WebGPU/Zig problems across projects — bind group layouts, uploading uniforms, mip generation, the coordinate-space gotchas. This post is where I write them down once, in the smallest form that still runs.

Roadmap

  • Hello triangle — the minimum zgpu setup that draws something.
  • Uniform buffers — uploading a per-frame MVP without tears.
  • Bind group layouts — what actually has to match what.
  • Compute — a falling-sand / cellular-automata pass in WGSL.
  • Depth + the NDC gotcha — WebGPU’s [0, 1] depth range vs OpenGL’s [-1, 1].
  • Mipmaps — generating them on the GPU with a compute shader.

A note on the projection gotcha

WebGPU clip space uses a depth range of z[0,1]z \in [0, 1], not OpenGL’s z[1,1]z \in [-1, 1]. If you bring an OpenGL-style perspective matrix over unchanged, everything is subtly wrong. The fix is a projection built for the [0,1][0, 1] convention:

P=[fa0000f0000zfznzfznzfznzf0010]f=cot ⁣(fovy2)P = \begin{bmatrix} \frac{f}{a} & 0 & 0 & 0 \\ 0 & f & 0 & 0 \\ 0 & 0 & \frac{z_f}{z_n - z_f} & \frac{z_n z_f}{z_n - z_f} \\ 0 & 0 & -1 & 0 \end{bmatrix} \qquad f = \cot\!\left(\tfrac{\text{fov}_y}{2}\right)

Hello triangle (skeleton)

// TODO: flesh this out — swapchain, pipeline, draw.
pub fn main() !void {
    const gctx = try zgpu.GraphicsContext.create(allocator, window, .{});
    defer gctx.destroy(allocator);

    // TODO: create render pipeline + bind group layout
    // TODO: per-frame: begin pass, set pipeline, draw(3), submit
}

More gems soon.