234 lines
5.3 KiB
TypeScript
234 lines
5.3 KiB
TypeScript
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()); |