feat: draw a red square

This commit is contained in:
Florian Sylvain
2025-04-05 23:37:22 +02:00
parent 95fa10f306
commit 10f67b8a11
9 changed files with 632 additions and 716 deletions
+85
View File
@@ -1 +1,86 @@
import "./style.css"
async function init() {
const canvas = document.querySelector<HTMLCanvasElement>("#canvas")
if (!canvas) {
throw new Error("Canvas element not found")
}
if (!navigator.gpu) {
throw new Error("WebGPU not supported on this browser.")
}
const adapter = await navigator.gpu.requestAdapter()
if (!adapter) {
throw new Error("No appropriate GPUAdapter found.")
}
const context = canvas.getContext("webgpu")
if (!context) {
throw new Error("Failed to get WebGPU context.")
}
const device = await adapter.requestDevice()
const canvasFormat = navigator.gpu.getPreferredCanvasFormat()
context.configure({ device, format: canvasFormat })
const encoder = device.createCommandEncoder()
const vertexBufferLayout = {
arrayStride: 8,
attributes: [
{
format: "float32x2" as GPUVertexFormat,
offset: 0,
shaderLocation: 0,
},
],
}
const cellShaderModule = device.createShaderModule({
label: "Cell shader",
code: (await import("./shaders/cell.wgsl?raw")).default,
})
const cellPipeline = device.createRenderPipeline({
label: "Cell pipeline",
layout: "auto",
vertex: {
module: cellShaderModule,
entryPoint: "vertexMain",
buffers: [vertexBufferLayout],
},
fragment: {
module: cellShaderModule,
entryPoint: "fragmentMain",
targets: [{ format: canvasFormat }],
},
})
const vertices = new Float32Array([
-0.8, -0.8, 0.8, -0.8, 0.8, 0.8, -0.8, -0.8, 0.8, 0.8, -0.8, 0.8,
])
const vertexBuffer = device.createBuffer({
label: "Cell vertices",
size: vertices.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
})
device.queue.writeBuffer(vertexBuffer, 0, vertices)
const pass = encoder.beginRenderPass({
colorAttachments: [
{
view: context.getCurrentTexture().createView(),
loadOp: "clear",
clearValue: [0.0, 0.0, 0.4, 1.0],
storeOp: "store",
},
],
})
pass.setPipeline(cellPipeline)
pass.setVertexBuffer(0, vertexBuffer)
pass.draw(vertices.length / 2)
pass.end()
device.queue.submit([encoder.finish()])
}
init().catch((error) => {
console.error("Failed to initialize the application:", error)
})
+9
View File
@@ -0,0 +1,9 @@
@vertex
fn vertexMain(@location(0) pos: vec2f) -> @builtin(position) vec4f {
return vec4f(pos, 0, 1);
}
@fragment
fn fragmentMain() -> @location(0) vec4f {
return vec4f(1, 0, 0, 1);
}
+9 -90
View File
@@ -1,96 +1,15 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
padding: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #222;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vanilla:hover {
filter: drop-shadow(0 0 2em #3178c6aa);
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
canvas {
width: 512px;
height: 512px;
display: block;
}