Skip to content

Instantly share code, notes, and snippets.

@bellbind
Last active April 3, 2022 13:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bellbind/a46bfb1c6c76246dcbe4f64a132dbb61 to your computer and use it in GitHub Desktop.
Save bellbind/a46bfb1c6c76246dcbe4f64a132dbb61 to your computer and use it in GitHub Desktop.
[WebGPU] render with no vertex buffer
<!doctype html>
<html>
<head>
<!-- IMPORTANT: The current Chrome requires some origin-trial token in <meta>.
To register origins at the last "WebGPU REGISTER" in https://developer.chrome.com/origintrials/
This token is for a Web Origin "http://localhost:8000" (maybe expired at Mar 31, 2022)
-->
<meta http-equiv="origin-trial"
content="AkIL+/THBoi1QEsWbX5SOuMpL6+KGAXKrZE5Bz6yHTuijzvKz2MznuLqE+MH4YSqRi/v1fDK/6JyFzgibTTeNAsAAABJeyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwMDAiLCJmZWF0dXJlIjoiV2ViR1BVIiwiZXhwaXJ5IjoxNjUyODMxOTk5fQ==" />
<meta http-equiv="origin-trial"
content="Akv07qcAop5MFaZYxJtHHjUuM8eV3GpbHkTeuhZo/4wsNjYnQ7GSGJyo7hRVZvpvyjYwilbJ8KbFVchI4O1DpA0AAABQeyJvcmlnaW4iOiJodHRwczovL2dpc3QuZ2l0aGFjay5jb206NDQzIiwiZmVhdHVyZSI6IldlYkdQVSIsImV4cGlyeSI6MTY1MjgzMTk5OX0=" />
<script src="./main.js" type="module"></script>
<style>@media(prefers-color-scheme: dark){:root {color-scheme: dark;}}</style>
<link rel="icon" href="data:image/x-icon;," />
</head>
<body>
<h1>(Notice: The origin-trial token in this page will be expired at May 15, 2022)</h1>
<canvas style="width: 80vmin; height: 80vmin; border: solid;" id="canvas"></canvas>
</body>
</html>
// Simple example for WebGPU API: https://www.w3.org/TR/webgpu/
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
// WGSL shaders: https://www.w3.org/TR/WGSL/
const wgsl = `
struct Uniforms {
t: u32;
corners: u32;
};
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
struct IO {
@builtin(position) pos: vec4<f32>;
@location(0) hsv: vec2<f32>;
};
@stage(vertex) fn vmain(@builtin(vertex_index) i: u32) -> IO {
let vid = i % 3u;
let tid = (i - vid) / 3u;
let cid = (uniforms.t + tid) % uniforms.corners;
let a = 360.0 / f32(uniforms.corners);
if (vid == 0u) {return IO(vec4<f32>(0.0, 0.0, 0.0, 1.0), vec2<f32>(f32(cid) * a, 0.0));}
let t = radians(f32(tid + vid - 1u) * a);
return IO(vec4<f32>(-sin(t), cos(t), 0.0, 1.0), vec2<f32>(f32(cid + vid - 1u) * a, 1.0));
}
fn hsv2rgb(h: f32, s: f32, v: f32, a: f32) -> vec4<f32> {
let h_ = (h % 360.0) / 60.0;
let f = modf(h_).fract;
let m = v * (1.0 - s);
let n = v * (1.0 - s * f);
let k = v * (1.0 - s * (1.0 - f));
if (h_ < 1.0) {return vec4<f32>(v, k, m, a);}
if (h_ < 2.0) {return vec4<f32>(n, v, m, a);}
if (h_ < 3.0) {return vec4<f32>(m, v, k, a);}
if (h_ < 4.0) {return vec4<f32>(m, n, v, a);}
if (h_ < 5.0) {return vec4<f32>(k, m, v, a);}
if (h_ < 6.0) {return vec4<f32>(v, m, n, a);}
return vec4<f32>(0.0, 0.0, 0.0, a);
}
@stage(fragment) fn fmain(io: IO) -> @location(0) vec4<f32> {
return hsv2rgb(io.hsv.x, io.hsv.y, 0.75, 1.0);
}
`;
const shader = device.createShaderModule({code: wgsl});
// gpu config for canvas
const canvas = document.getElementById("canvas");
const gpu = canvas.getContext("webgpu");
const format = gpu.getPreferredFormat(adapter);
gpu.configure({device, format, size: [canvas.width, canvas.height]});
// pipeline
const pipeline = device.createRenderPipeline({
primitive: {topology: "triangle-list", cullMode: "back"},
vertex: {module: shader, entryPoint: "vmain", buffers: []},
fragment: {module: shader, entryPoint: "fmain", targets: [{format}]},
});
// bind group
const uniforms = new Uint32Array([0, 3]);
const uniformsBuffer = device.createBuffer({usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, size: uniforms.byteLength});
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{binding: 0, resource: {buffer: uniformsBuffer}}],
});
// render with no vertex buffer
const corners = 64;
const render = (t) => {
const c = 3 + Math.round((corners - 3) / 2 * (-Math.cos(t / 80) + 1));
[uniforms[0], uniforms[1]] = [t / 10, c];
device.queue.writeBuffer(uniformsBuffer, 0, uniforms.buffer);
const view = gpu.getCurrentTexture().createView();
const clearValue = {r: 0, g: 0, b: 0, a: 1};
const renderPass = {colorAttachments: [{view, loadOp: "clear", clearValue, loadValue: clearValue, storeOp: "store"}]}; //[chrome-99] loadValue
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPass);
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.draw(3 * c, 1);
(passEncoder.end ?? passEncoder.endPass).call(passEncoder); //[chrome-99] endPass
device.queue.submit([commandEncoder.finish()]);
};
(function loop(t) {
render(t);
requestAnimationFrame(() => loop(t + 1));
})(0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment