The shelf now stands in 3D: six covers lean alternately left and right, with a perspective divide by z, so neighbors meet in a zig-zag and the depth test decides which is in front where they overlap. The same scene renders into two canvases, without and with 4x MSAA, and a 2D canvas magnifies the same 72 x 32 pixel crop of each four times:
<canvas id="plain" width="290" height="120"></canvas>
<canvas id="msaa" width="290" height="120"></canvas>
<canvas id="zoom" width="590" height="128"></canvas>
<script type="module">
const code = /* wgsl */ `
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, @location(0) shade: vec3f }
@vertex fn vs(@builtin(vertex_index) v: u32, @builtin(instance_index) b: u32) -> Out {
let uv = array(vec2f(0, 0), vec2f(1, 0), vec2f(0, 1), vec2f(1, 0), vec2f(1, 1),
vec2f(0, 1))[v];
let angle = select(-0.7, 0.7, b % 2 == 0); // covers lean left and right
let x = (uv.x - 0.5) * 1.2;
let world = vec3f(x * cos(angle) - 2.25 + f32(b) * 0.9, uv.y * 1.8 - 0.9,
x * sin(angle) + 4); // neighbors overlap slightly
let clip = vec4f(world.x * 2.6 / 2.4, world.y * 2.6, (world.z - 1) / 9 * world.z, world.z);
let light = 0.65 + 0.35 * uv.y - 0.25 * f32(uv.x < 0.1);
return Out(clip, tints[b] * light); // perspective: GPU divides by z
}
@fragment fn fs(in: Out) -> @location(0) vec4f { return vec4f(in.shade, 1); }`;
const device = await (await navigator.gpu.requestAdapter()).requestDevice();
const format = navigator.gpu.getPreferredCanvasFormat();
const module = device.createShaderModule({ code });
const pipelines = await Promise.all([1, 4].map((count) => device.createRenderPipelineAsync({
layout: 'auto', vertex: { module }, fragment: { module, targets: [{ format }] },
depthStencil: { format: 'depth24plus', depthWriteEnabled: true, depthCompare: 'less' },
multisample: { count } })));
function drawShelf(canvas, pipeline, sampleCount) {
const context = canvas.getContext('webgpu');
context.configure({ device, format });
const size = [canvas.width, canvas.height], usage = GPUTextureUsage.RENDER_ATTACHMENT;
const depth = device.createTexture({ size, sampleCount, format: 'depth24plus', usage });
const target = context.getCurrentTexture().createView();
const msaa = sampleCount > 1 && device.createTexture({ size, sampleCount, format, usage });
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{ clearValue: [0.96, 0.94, 0.90, 1], loadOp: 'clear',
view: msaa ? msaa.createView() : target, resolveTarget: msaa ? target : undefined,
storeOp: msaa ? 'discard' : 'store' }], // MSAA: keep only the resolved image
depthStencilAttachment: { view: depth.createView(), depthClearValue: 1,
depthLoadOp: 'clear', depthStoreOp: 'discard' } });
pass.setPipeline(pipeline), pass.draw(6, 6), pass.end();
device.queue.submit([encoder.finish()]);
}
drawShelf(plain, pipelines[0], 1);
drawShelf(msaa, pipelines[1], 4);
const ctx = zoom.getContext('2d'); // same task: before the canvases present
ctx.imageSmoothingEnabled = false; // real pixels, magnified 4 times
ctx.drawImage(plain, 40, 12, 72, 32, 0, 0, 288, 128);
ctx.drawImage(msaa, 40, 12, 72, 32, 302, 0, 288, 128);
await device.queue.onSubmittedWorkDone();
window.__done = true;
</script>
Without MSAA (left crop) the top edges climb in one-pixel steps; with 4x MSAA (right) edge pixels blend cover and background by covered samples. The 2D copy runs in the same task as the submits: after a WebGPU canvas presents, drawImage() no longer sees its frame.