Skip to content

Commit

Permalink
feat: webgpu fractal cube example
Browse files Browse the repository at this point in the history
  • Loading branch information
load1n9 committed Jan 31, 2024
1 parent 44afb5e commit 9202f24
Show file tree
Hide file tree
Showing 2 changed files with 310 additions and 1 deletion.
3 changes: 2 additions & 1 deletion deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"example:webgpu": "deno run -A --unstable-ffi --unstable-webgpu examples/webgpu.ts",
"example:webgpu-triangle": "deno run -A --unstable-ffi --unstable-webgpu examples/webgpu-triangle.ts",
"example:webgpu-cube": "deno run -A --unstable-ffi --unstable-webgpu examples/webgpu-cube.ts",
"example:webgpu-cube-floating": "deno run -A --unstable-ffi --unstable-webgpu examples/webgpu-cube-floating.ts"
"example:webgpu-cube-floating": "deno run -A --unstable-ffi --unstable-webgpu examples/webgpu-cube-floating.ts",
"example:webgpu-fractal-cube": "deno run -A --unstable-ffi --unstable-webgpu examples/webgpu-fractal-cube.ts"
},

"lint": {
Expand Down
308 changes: 308 additions & 0 deletions examples/webgpu-fractal-cube.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
// ported from https://github.com/webgpu/webgpu-samples/blob/main/src/sample/rotatingCube/main.ts
import { mat4, vec3 } from "npm:wgpu-matrix";
import { createWindow, mainloop } from "../mod.ts";

const basicVertWGSL = `
struct Uniforms {
modelViewProjectionMatrix : mat4x4<f32>,
}
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
}
@vertex
fn main(
@location(0) position : vec4<f32>,
@location(1) uv : vec2<f32>
) -> VertexOutput {
var output : VertexOutput;
output.Position = uniforms.modelViewProjectionMatrix * position;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0));
return output;
}
`;

const sampleSelfWGSL = `
@binding(1) @group(0) var mySampler: sampler;
@binding(2) @group(0) var myTexture: texture_2d<f32>;
@fragment
fn main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
let texColor = textureSample(myTexture, mySampler, fragUV * 0.8 + vec2(0.1));
let f = select(1.0, 0.0, length(texColor.rgb - vec3(0.5)) < 0.01);
return f * texColor + (1.0 - f) * fragPosition;
}
`;

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter!.requestDevice();

const cubeVertexSize = 4 * 10;
const cubePositionOffset = 0;
const cubeUVOffset = 4 * 8;
const cubeVertexCount = 36;

// deno-fmt-ignore
export const cubeVertexArray = new Float32Array([
// float4 position, float4 color, float2 uv,
1, -1, 1, 1, 1, 0, 1, 1, 0, 1,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 0,
1, -1, -1, 1, 1, 0, 0, 1, 0, 0,
1, -1, 1, 1, 1, 0, 1, 1, 0, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 0,

1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, -1, 1, 1, 1, 0, 1, 1, 1, 1,
1, -1, -1, 1, 1, 0, 0, 1, 1, 0,
1, 1, -1, 1, 1, 1, 0, 1, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, -1, -1, 1, 1, 0, 0, 1, 1, 0,

-1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, -1, 1, 1, 1, 0, 1, 1, 0,
-1, 1, -1, 1, 0, 1, 0, 1, 0, 0,
-1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
1, 1, -1, 1, 1, 1, 0, 1, 1, 0,

-1, -1, 1, 1, 0, 0, 1, 1, 0, 1,
-1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
-1, -1, -1, 1, 0, 0, 0, 1, 0, 0,
-1, -1, 1, 1, 0, 0, 1, 1, 0, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,

1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
-1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 0,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 0,
1, -1, 1, 1, 1, 0, 1, 1, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,

1, -1, -1, 1, 1, 0, 0, 1, 0, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
1, 1, -1, 1, 1, 1, 0, 1, 0, 0,
1, -1, -1, 1, 1, 0, 0, 1, 0, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
]);

const window = createWindow({
title: "Deno Window Manager",
width: 512,
height: 512,
resizable: true,
});

const { width, height } = window.framebufferSize;

const surface = window.windowSurface();

const context = surface.getContext("webgpu");

const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
// alphaMode: "premultiplied",
width,
height,
});

const verticesBuffer = device.createBuffer({
size: cubeVertexArray.byteLength,
usage: GPUBufferUsage.VERTEX,
mappedAtCreation: true,
});
new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
verticesBuffer.unmap();

const pipeline = device.createRenderPipeline({
layout: "auto",
vertex: {
module: device.createShaderModule({
code: basicVertWGSL,
}),
entryPoint: "main",
buffers: [
{
arrayStride: cubeVertexSize,
attributes: [
{
// position
shaderLocation: 0,
offset: cubePositionOffset,
format: "float32x4",
},
{
// uv
shaderLocation: 1,
offset: cubeUVOffset,
format: "float32x2",
},
],
},
],
},
fragment: {
module: device.createShaderModule({
code: sampleSelfWGSL,
}),
entryPoint: "main",
targets: [
{
format: presentationFormat,
},
],
},
primitive: {
topology: "triangle-list",

// Backface culling since the cube is solid piece of geometry.
// Faces pointing away from the camera will be occluded by faces
// pointing toward the camera.
cullMode: "back",
},

// Enable depth testing so that the fragment closest to the camera
// is rendered in front.
depthStencil: {
depthWriteEnabled: true,
depthCompare: "less",
format: "depth24plus",
},
});

const depthTexture = device.createTexture({
size: [width, height],
format: "depth24plus",
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});

const uniformBufferSize = 4 * 16; // 4x4 matrix
const uniformBuffer = device.createBuffer({
size: uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});

const cubeTexture = device.createTexture({
size: [width, height],
format: presentationFormat,
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
});

const sampler = device.createSampler({
magFilter: "linear",
minFilter: "linear",
});

const uniformBindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: uniformBuffer,
},
},
{
binding: 1,
resource: sampler,
},
{
binding: 2,
resource: cubeTexture.createView(),
},
],
});
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: undefined!, // Assigned later

clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 },
loadOp: "clear",
storeOp: "store",
},
],
depthStencilAttachment: {
view: depthTexture.createView(),

depthClearValue: 1.0,
depthLoadOp: "clear",
depthStoreOp: "store",
},
};

const aspect = width / height;
const projectionMatrix = mat4.perspective(
(2 * Math.PI) / 5,
aspect,
1,
100.0,
);
const modelViewProjectionMatrix = mat4.create();

function getTransformationMatrix() {
const viewMatrix = mat4.identity();
mat4.translate(viewMatrix, vec3.fromValues(0, 0, -4), viewMatrix);
const now = Date.now() / 1000;
mat4.rotate(
viewMatrix,
vec3.fromValues(Math.sin(now), Math.cos(now), 0),
1,
viewMatrix,
);

mat4.multiply(projectionMatrix, viewMatrix, modelViewProjectionMatrix);

return modelViewProjectionMatrix as Float32Array;
}

mainloop(() => {
const transformationMatrix = getTransformationMatrix();
device.queue.writeBuffer(
uniformBuffer,
0,
transformationMatrix.buffer,
transformationMatrix.byteOffset,
transformationMatrix.byteLength,
);

const swapChainTexture = context.getCurrentTexture();
// prettier-ignore
renderPassDescriptor.colorAttachments[0]!.view = swapChainTexture
.createView();

const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, uniformBindGroup);
passEncoder.setVertexBuffer(0, verticesBuffer);
passEncoder.draw(cubeVertexCount);
passEncoder.end();

// Copy the rendering results from the swapchain into |cubeTexture|.
commandEncoder.copyTextureToTexture(
{
texture: swapChainTexture,
},
{
texture: cubeTexture,
},
[width, height],
);

device.queue.submit([commandEncoder.finish()]);
surface.present();
}, false);

0 comments on commit 9202f24

Please sign in to comment.