diff --git a/docs/01-rainbow-triangle.md b/docs/01-rainbow-triangle.md index a37cc12..1796d4f 100644 --- a/docs/01-rainbow-triangle.md +++ b/docs/01-rainbow-triangle.md @@ -572,8 +572,22 @@ and delivers the interpolated value to every fragment. **`@fragment fn fs_main(input: VertexOutput)`** — `@fragment` declares the fragment shader entry point. `input` is the rasterizer's interpolated output from the vertex shader. Every `@location(n)` field in `VertexOutput` is now -pre-blended. The `@builtin(position)` field is not interpolated — it is the -original vertex position. +pre-blended with barycentric weights. + +> **Key insight — TWO `@builtin(position)` builtins, zero connection:** +> Vertex `@builtin(position)` and fragment `@builtin(position)` are two +> completely separate builtins that happen to share the same name. The vertex +> shader outputs clip-space coordinates into `@builtin(position)` for the +> rasterizer to perform perspective division and viewport transform. The +> fragment shader receives an entirely different `@builtin(position)` injected +> by the fragment stage, providing framebuffer pixel coordinates: `x`/`y` are +> the pixel center within the viewport, `z` is the depth value (typically +> [0, 1]), and `w` is the interpolated reciprocal of the vertex clip-space +> w-coordinate (1/w). The vertex shader's position output is NOT passed to the +> fragment shader's position input. They are independent builtins from +> different pipeline stages. If you need to pass data from vertex to fragment +> with interpolation, use `@location(N)` on regular struct fields — which is +> exactly what `vertex_color` does in our shader. **`-> @location(0) vec4`** — The fragment shader must output at least one color value at `@location(0)`. This number must match the corresponding color