import shaderSource from './shaders/shader.wgsl?raw'; import { QuadGeometry } from './geometry'; import { Texture } from './texture'; class Renderer{ private device! : GPUDevice; private context! : GPUCanvasContext; private pipeline! : GPURenderPipeline; private postitionBuffer! : GPUBuffer; private colorBuffer! : GPUBuffer; private texCoordBuffer! : GPUBuffer; private textureBindGroup! : GPUBindGroup; private testTexture! : Texture; public async initialize() { const canvas = document.getElementById('canvas') as HTMLCanvasElement; this.context = canvas.getContext('webgpu')!; if(!this.context){ alert('WebGPU not supported'); return; } const adapter = await navigator.gpu.requestAdapter( { powerPreference: 'low-power' } ); if(!adapter) { alert('No WebGPU adapter found'); return; } this.device = await adapter.requestDevice() this.context.configure({ device: this.device, format: navigator.gpu.getPreferredCanvasFormat() }) this.testTexture = await Texture.createTextureFromUrl(this.device, 'assets/UVEditorColorGrid.png'); this.prepareModel(); const geometry = new QuadGeometry(); this.postitionBuffer = this.CreateBuffer(new Float32Array(geometry.positions)); this.colorBuffer = this.CreateBuffer(new Float32Array(geometry.colors)); this.texCoordBuffer = this.CreateBuffer(new Float32Array(geometry.texCoords)); } private CreateBuffer(data: Float32Array) { const buffer = this.device.createBuffer({ size: data.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, mappedAtCreation: true }); new Float32Array(buffer.getMappedRange()).set(data); buffer.unmap(); return buffer; } private prepareModel() { const shaderModule = this.device.createShaderModule({code: shaderSource}); const postitionBufferLayout : GPUVertexBufferLayout = { arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT, attributes: [ { shaderLocation: 0, offset: 0, format: 'float32x2' } ], stepMode: 'vertex' } const colorBufferLayout : GPUVertexBufferLayout = { arrayStride: 3 * Float32Array.BYTES_PER_ELEMENT, attributes: [ { shaderLocation: 1, offset: 0, format: 'float32x3' }] } const textureCoordBufferLayout : GPUVertexBufferLayout = { arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT, attributes: [ { shaderLocation: 2, offset: 0, format: 'float32x2' }], stepMode: 'vertex' } const vertexState: GPUVertexState = { module : shaderModule, entryPoint: "VertexMain", buffers: [ postitionBufferLayout, colorBufferLayout, textureCoordBufferLayout ] } const fragmentState: GPUFragmentState = { module : shaderModule, entryPoint: "FragmentMain", targets: [ { format: navigator.gpu.getPreferredCanvasFormat(), blend: { color: { srcFactor: 'one', dstFactor: 'zero', operation: 'add' }, alpha: { srcFactor: 'one', dstFactor: 'zero', operation: 'add' } } } ] } const textureBindGroupLayout = this.device.createBindGroupLayout({ entries: [ { binding: 0, visibility: GPUShaderStage.FRAGMENT, sampler: {} }, { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: {} } ] }); const pipelineLayout = this.device.createPipelineLayout({ bindGroupLayouts: [ textureBindGroupLayout ] }); this.textureBindGroup = this.device.createBindGroup({ layout: textureBindGroupLayout, entries: [ { binding: 0, resource: this.testTexture.sampler }, { binding: 1, resource: this.testTexture.texture.createView() } ] }); this.pipeline = this.device.createRenderPipeline({ vertex: vertexState, fragment: fragmentState, primitive:{ topology: 'triangle-list', }, layout: pipelineLayout }); } public draw() { const commandEncoder = this.device.createCommandEncoder(); const textureView = this.context.getCurrentTexture().createView(); const renderPassDescriptor:GPURenderPassDescriptor = { colorAttachments:[{ view: textureView, clearValue: {r: 0, g: 0, b: 0, a: 1}, loadOp: 'clear', storeOp:'store' }] } const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); //Draw here passEncoder.setPipeline(this.pipeline ); passEncoder.setVertexBuffer(0, this.postitionBuffer); passEncoder.setVertexBuffer(1, this.colorBuffer); passEncoder.setVertexBuffer(2, this.texCoordBuffer); passEncoder.setBindGroup(0, this.textureBindGroup) passEncoder.draw(6); passEncoder.end(); this.device.queue.submit([commandEncoder.finish()]); } } const renderer = new Renderer(); renderer.initialize() .then(()=> renderer.draw());