import { getPathText } from "./common.js" // Triangle vertices, one vertex per line. Hand-transcribed from a // .obj file exported from Blender to this form. // // I've also written myriad scripts to do this automatically for // larger models. // // Float32Array is a fairly convenient way to convert floating point // literals to bytes. const triangleVertices = new Float32Array([ // [position ] [texture ] [normal ] 0.0, 1.0, 0.0, 0.5, 1.0, 0.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, ]); class TriangleRenderer { constructor(device, canvasFormat, wgsl) { const label = "TriangleRenderer"; ////////////////////////////////////////////////////////////////////// // shader module ////////////////////////////////////////////////////////////////////// const shaderModule = device.createShaderModule({ label: `${label} shader`, code: wgsl, }); ////////////////////////////////////////////////////////////////////// // pipeline ////////////////////////////////////////////////////////////////////// // "bind group layouts" are a declaration of which `var` bindings // exist in the shader programs loaded by this pipeline // // None are used in triangle.wgsl, so this list is empty. const bindGroupLayouts = [ ]; const pipelineLayout = device.createPipelineLayout({ label: `${label} pipeline layout`, bindGroupLayouts: bindGroupLayouts, }); // vertexBufferLayouts describes the position of each element for // each buffer. There is only one vertex buffer used in this demo, // so there is only one vertex buffer layout in this list. // // shaderLocation matches the location numbers defined in // triangle.wgsl. // // "offset" is the starting offset in bytes, 4 bytes per floating // point number. This matches the arrangement in bytes of the // triangleVertices Float32Array. const arrayStride = (4 * 3) + (4 * 2) + (4 * 3); const vertexBufferLayouts = [ { arrayStride: arrayStride, attributes: [{ format: "float32x3", offset: 0, shaderLocation: 0, }, { format: "float32x2", offset: 12, shaderLocation: 1, }, { format: "float32x3", offset: 20, shaderLocation: 2, }], }, ]; this.renderPipeline = device.createRenderPipeline({ label: `${label} render pipeline`, layout: pipelineLayout, vertex: { module: shaderModule, entryPoint: "vertexMain", buffers: vertexBufferLayouts, }, fragment: { module: shaderModule, entryPoint: "fragmentMain", targets: [{ format: canvasFormat, }] }, primitive: { topology: "triangle-list", }, depthStencil: { depthWriteEnabled: true, depthCompare: "less", format: "depth24plus", }, }); ////////////////////////////////////////////////////////////////////// // bind group ////////////////////////////////////////////////////////////////////// // there are no bind groups, because triangle.wgsl doesn't use any. ////////////////////////////////////////////////////////////////////// // buffer ////////////////////////////////////////////////////////////////////// this.vertexBuffer = device.createBuffer({ label: `${label} vertex buffer`, size: triangleVertices.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, }); // copy the (system RAM) triangleVertices to the (gpu RAM) vertexBuffer. device.queue.writeBuffer(this.vertexBuffer, 0, triangleVertices); } render(renderPass) { renderPass.setPipeline(this.renderPipeline); renderPass.setVertexBuffer(0, this.vertexBuffer); const vertexCount = 3; renderPass.draw(vertexCount); } } // This "loadTriangleRenderer" function is separate from the // constructor because Javascript class constructors can't be async // functions. async function loadTriangleRenderer(device, canvasFormat) { const triangleWgsl = await getPathText("triangle.wgsl"); const renderer = new TriangleRenderer(device, canvasFormat, triangleWgsl); return renderer; } export { loadTriangleRenderer };