Last active
January 19, 2024 22:29
-
-
Save ccincotti3/377291ec08fdc7426dce77a4203905bf to your computer and use it in GitHub Desktop.
WebGPU Triangle 2022
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Carmen Cincotti - WebGPU Triangle</title> | |
<style> | |
canvas { | |
width: 100%; | |
height: 100%; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="canvas-container"></canvas> | |
<script> | |
const init = async () => { | |
// ~~ INITIALIZE ~~ Make sure we can initialize WebGPU | |
if (!navigator.gpu) { | |
console.error( | |
"WebGPU cannot be initialized - navigator.gpu not found" | |
); | |
return null; | |
} | |
const adapter = await navigator.gpu.requestAdapter(); | |
if (!adapter) { | |
console.error("WebGPU cannot be initialized - Adapter not found"); | |
return null; | |
} | |
const device = await adapter.requestDevice(); | |
device.lost.then(() => { | |
console.error("WebGPU cannot be initialized - Device has been lost"); | |
return null; | |
}); | |
const canvas = document.getElementById("canvas-container"); | |
const context = canvas.getContext("webgpu"); | |
if (!context) { | |
console.error( | |
"WebGPU cannot be initialized - Canvas does not support WebGPU" | |
); | |
return null; | |
} | |
// ~~ CONFIGURE THE SWAP CHAIN ~~ | |
const devicePixelRatio = window.devicePixelRatio || 1; | |
const presentationSize = [ | |
canvas.clientWidth * devicePixelRatio, | |
canvas.clientHeight * devicePixelRatio, | |
]; | |
const presentationFormat = context.getPreferredFormat(adapter); | |
context.configure({ | |
device, | |
format: presentationFormat, | |
size: presentationSize, | |
}); | |
// ~~ SETUP VERTICES (position (vec3<f32>), color(vec4<i32>)) ~~ | |
// Pack them all into one array | |
// Each vertex has a position and a color packed in memory in X Y Z W R G B A order | |
const vertices = new Float32Array([ | |
-1.0, -1.0, 0, 1, 1, 0, 0, 1, -0.0, 1.0, 0, 1, 0, 1, 0, 1, 1.0, -1.0, | |
0, 1, 0, 0, 1, 1, | |
]); | |
const vertexBuffer = device.createBuffer({ | |
size: vertices.byteLength, | |
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, | |
mappedAtCreation: true, | |
}); | |
new Float32Array(vertexBuffer.getMappedRange()).set(vertices); | |
vertexBuffer.unmap(); | |
const vertexBuffersDescriptors = [ | |
{ | |
attributes: [ | |
{ | |
shaderLocation: 0, | |
offset: 0, | |
format: "float32x4", | |
}, | |
{ | |
shaderLocation: 1, | |
offset: 16, | |
format: "float32x4", | |
}, | |
], | |
arrayStride: 32, | |
stepMode: "vertex", | |
}, | |
]; | |
// ~~ DEFINE BASIC SHADERS ~~ | |
const shaderModule = device.createShaderModule({ | |
code: ` | |
struct VertexOut { | |
@builtin(position) position : vec4<f32>, | |
@location(0) color : vec4<f32>, | |
}; | |
@stage(vertex) | |
fn vertex_main(@location(0) position: vec4<f32>, | |
@location(1) color: vec4<f32>) -> VertexOut | |
{ | |
var output : VertexOut; | |
output.position = position; | |
output.color = color; | |
return output; | |
} | |
@stage(fragment) | |
fn fragment_main(fragData: VertexOut) -> @location(0) vec4<f32> | |
{ | |
return fragData.color; | |
} | |
`, | |
}); | |
// ~~ CREATE RENDER PIPELINE ~~ | |
const pipeline = device.createRenderPipeline({ | |
vertex: { | |
module: shaderModule, | |
entryPoint: "vertex_main", | |
buffers: vertexBuffersDescriptors, | |
}, | |
fragment: { | |
module: shaderModule, | |
entryPoint: "fragment_main", | |
targets: [ | |
{ | |
format: presentationFormat, | |
}, | |
], | |
}, | |
primitive: { | |
topology: "triangle-list", | |
}, | |
}); | |
// ~~ CREATE RENDER PASS DESCRIPTOR ~~ | |
const renderPassDescriptor = { | |
colorAttachments: [ | |
{ | |
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, | |
loadOp: "clear", | |
storeOp: "store", | |
}, | |
], | |
}; | |
// ~~ Define render loop ~~ | |
function frame() { | |
renderPassDescriptor.colorAttachments[0].view = context | |
.getCurrentTexture() | |
.createView(); | |
const commandEncoder = device.createCommandEncoder(); | |
const passEncoder = | |
commandEncoder.beginRenderPass(renderPassDescriptor); | |
passEncoder.setPipeline(pipeline); | |
passEncoder.setVertexBuffer(0, vertexBuffer); | |
passEncoder.draw(3); | |
passEncoder.end(); | |
device.queue.submit([commandEncoder.finish()]); | |
requestAnimationFrame(frame); | |
} | |
requestAnimationFrame(frame); | |
}; | |
init(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To anyone who stumbles upon this, this code doesn't work there are multiple errors with it find newer version in creators profile.