Spaces:
Running
Running
File size: 4,518 Bytes
6d53e86 bc152ba 1889a23 d13e19f 0741357 bc152ba 6d53e86 d13e19f 6d53e86 cf65f18 6d53e86 d13e19f 6d53e86 551d60d 6d53e86 551d60d d13e19f 6d53e86 d13e19f 6d53e86 551d60d 6d53e86 551d60d 6d53e86 d13e19f 6d53e86 0f567ca d13e19f 6d53e86 d13e19f 6d53e86 d13e19f 6d53e86 551d60d 6d53e86 d13e19f 6d53e86 d13e19f 6d53e86 d13e19f 6d53e86 d13e19f 0f567ca d13e19f 0f567ca d13e19f 0f567ca 6d53e86 e31d2a8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
import { mat4 } from 'https://webgpufundamentals.org/3rdparty/wgpu-matrix.module.js';
import { initializeWebGPU } from './wgpu-devices.js';
import { createState } from './wgpu-state.js';
import { fetchShaderCode, generateGlyphTextureAtlas, createTextureFromSource } from './wgpu-utility.js';
import { createPipeline } from './wgpu-pipeline.js';
import { generateGlyphVerticesForText } from './wgpu-text.js';
import { config } from './wgpu-config.js';
import { CANVAS, CTX, COLORS, RENDER_PASS_DESCRIPTOR } from './wgpu-constants.js';
const canvas = document.querySelector('canvas');
const state = createState(config);
async function main() {
const adapter = await navigator.gpu?.requestAdapter();
const { device, context, presentationFormat } = await initializeWebGPU(navigator, adapter, canvas);
if (!device) return;
state.device = device;
const shaderCode = await fetchShaderCode('shaders.wgsl');
const vertexSize = config.floatsPerVertex * 4;
state.pipeline = await createPipeline(state.device, presentationFormat, vertexSize, shaderCode);
const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
document.body.appendChild(glyphCanvas);
glyphCanvas.style.backgroundColor = '#222';
const vertexBufferSize = config.maxGlyphs * config.vertsPerGlyph * vertexSize;
state.vertexBuffer = state.device.createBuffer({
label: 'vertices',
size: vertexBufferSize,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
state.indexBuffer = state.device.createBuffer({
label: 'indices',
size: config.maxGlyphs * config.vertsPerGlyph * 4,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
});
const indices = Array.from({ length: config.maxGlyphs * 6 }, (_, i) => {
const ndx = Math.floor(i / 6) * 4;
return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
});
state.device.queue.writeBuffer(state.indexBuffer, 0, new Uint32Array(indices));
const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, config, glyphCanvas);
state.device.queue.writeBuffer(state.vertexBuffer, 0, vertexData);
state.texture = createTextureFromSource(state.device, glyphCanvas, { mips: true });
state.sampler = state.device.createSampler({
minFilter: 'linear',
magFilter: 'linear'
});
state.uniformBuffer = state.device.createBuffer({
label: 'uniforms for quad',
size: config.uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});
state.matrix = state.uniformValues.subarray(0, 16);
state.bindGroup = state.device.createBindGroup({
layout: state.pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: state.sampler },
{ binding: 1, resource: state.texture.createView() },
{ binding: 2, resource: { buffer: state.uniformBuffer } },
],
});
state.numGlyphs = numGlyphs;
state.width = width;
state.height = height;
requestAnimationFrame((time) => render(time, mat4, context, state, RENDER_PASS_DESCRIPTOR));
}
function render(time, mat4, context, state, RENDER_PASS_DESCRIPTOR) {
time *= config.time.phase;
const fov = 60 * Math.PI / 180;
const aspect = canvas.clientWidth / canvas.clientHeight;
const projectionMatrix = mat4.perspective(fov, aspect, config.render.zNear, config.render.zFar);
const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = context.getCurrentTexture().createView();
const encoder = state.device.createCommandEncoder();
const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
pass.setPipeline(state.pipeline);
mat4.rotateY(viewProjectionMatrix, time, state.matrix);
mat4.translate(state.matrix, [-state.width / 2, -state.height / 2, 0], state.matrix);
state.device.queue.writeBuffer(state.uniformBuffer, 0, state.uniformValues);
pass.setBindGroup(0, state.bindGroup);
pass.setVertexBuffer(0, state.vertexBuffer);
pass.setIndexBuffer(state.indexBuffer, 'uint32');
pass.drawIndexed(state.numGlyphs * 6);
pass.end();
state.device.queue.submit([encoder.finish()]);
requestAnimationFrame((t) => render(t, mat4, context, state, RENDER_PASS_DESCRIPTOR));
}
main();
|