Fragment Shader Built-ins

A fragment's position is not what the vertex shader wrote: after the divide by w and the viewport transform, xy are framebuffer pixel coordinates at pixel centers (0.5, 1.5, ...), z is depth and w is 1/w. front_facing reports the winding seen on screen, and primitive_index (the primitive-index feature) numbers the triangles of the draw. Outputs are colors at @location(n), frag_depth and sample_mask (Depth and Multisampling). This strip of ten triangles twists after vertex 5:

Fragment built-ins, and a clip distance, on a twisted triangle stripHTMLLive
<canvas id="gpu" width="480" height="110"></canvas>
<script type="module">
const code = /* wgsl */ `
enable clip_distances;
enable primitive_index;
const tints = array(vec3f(.08, .40, .75), vec3f(.18, .49, .20), vec3f(.94, .42, 0),
                    vec3f(.42, .11, .60), vec3f(.22, .28, .31), vec3f(.78, .16, .16));
struct Out { @builtin(position) pos: vec4f, @builtin(clip_distances) clip: array<f32, 1> }
@vertex fn vs(@builtin(vertex_index) v: u32) -> Out {
  let top = (v % 2 == 0) != (v >= 6);             // the ribbon twists after vertex 5
  let p = vec2f(-0.95 + f32(v / 2) * 0.38, select(-0.85, 0.85, top));
  return Out(vec4f(p, 0, 1), array(1.1 - p.x - p.y));   // clip the top-right corner
}
@fragment fn fs(@builtin(position) p: vec4f, @builtin(front_facing) front: bool,
                @builtin(primitive_index) prim: u32) -> @location(0) vec4f {
  let color = tints[prim % 6] * select(1.0, 0.8, u32(p.y) % 16 < 8);   // pixel stripes
  return vec4f(select(color * 0.35 + 0.6, color, front), 1);          // back faces pale
}`;
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice({
  requiredFeatures: ['clip-distances', 'primitive-index'] });
const context = gpu.getContext('webgpu'), format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format });
const module = device.createShaderModule({ code });
const pipeline = device.createRenderPipeline({ layout: 'auto', vertex: { module },
  fragment: { module, targets: [{ format }] }, primitive: { topology: 'triangle-strip' } });
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({ colorAttachments: [{ loadOp: 'clear', storeOp: 'store',
  view: context.getCurrentTexture().createView(), clearValue: [0.96, 0.94, 0.90, 1] }] });
pass.setPipeline(pipeline), pass.draw(12), pass.end();   // 12 vertices: 10 triangles
device.queue.submit([encoder.finish()]);
await device.queue.onSubmittedWorkDone();
window.__done = true;
</script>
Browser output of Listing 4.49
Browser output of 49

Five triangles face you, the rest show their pale backs, and the clip distance cuts the top-right corner. The stripes ignore the triangles because p.y is in pixels, which suits dithering and G-buffer reads.