feat(webgpu): Add two cubes example (#1558)

This commit is contained in:
Ib Green 2021-12-20 18:49:28 -08:00 committed by GitHub
parent b923efb3c6
commit 352be62c8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 329 additions and 54 deletions

View File

@ -45,36 +45,6 @@ fn main([[location(0)]] fragUV: vec2<f32>,
}
};
// const vertexBufferLayout = {
// arrayStride: cubeVertexSize,
// attributes: [
// {
// // position
// shaderLocation: 0,
// offset: cubePositionOffset,
// format: 'float32x4',
// },
// {
// // uv
// shaderLocation: 1,
// offset: cubeUVOffset,
// format: 'float32x2',
// },
// ],
// };
// const uniformBindGroup = device.handle.createBindGroup({
// layout: pipeline.handle.getBindGroupLayout(0),
// entries: [
// {
// binding: 0,
// resource: {
// buffer: uniformBuffer.handle,
// },
// },
// ],
// });
const UNIFORM_BUFFER_SIZE = 4 * 16; // 4x4 matrix
export async function init(canvas: HTMLCanvasElement, language: 'glsl' | 'wgsl') {
@ -84,8 +54,8 @@ export async function init(canvas: HTMLCanvasElement, language: 'glsl' | 'wgsl')
const positionBuffer = device.createBuffer({id: 'cube-positions', data: cubePositions});
const uvBuffer = device.createBuffer({id: 'cube-uvs', data: cubeUVs});
const uniformBuffer = device.createBuffer({
id: 'uniforms',
byteLength: UNIFORM_BUFFER_SIZE,
// TODO - use API constants instead of WebGPU constants
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
@ -141,26 +111,3 @@ export async function init(canvas: HTMLCanvasElement, language: 'glsl' | 'wgsl')
}
init(document.getElementById('canvas') as HTMLCanvasElement, 'wgsl');
/*
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, );
function getTransformationMatrix() {
const viewMatrix = mat4.create();
mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -4));
const now = Date.now() / 1000;
mat4.rotate(
viewMatrix,
viewMatrix,
1,
vec3.fromValues(Math.sin(now), Math.cos(now), 0)
);
const modelViewProjectionMatrix = mat4.create();
mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
return modelViewProjectionMatrix as Float32Array;
}
*/

View File

@ -0,0 +1,141 @@
import {AttributeBinding, RenderPipelineParameters} from '@luma.gl/api';
import {Model, WebGPUDevice} from '@luma.gl/webgpu';
import {Matrix4} from '@math.gl/core';
import {
cubePositions,
cubeUVs,
cubeVertexCount
} from './cube';
export const title = 'Two Cubes';
export const description = 'Shows usage of multiple uniform buffers.';
/** Provide both GLSL and WGSL shaders */
const SHADERS = {
wgsl: {
vertex: `
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>;
};
[[stage(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<f32>(1.0, 1.0, 1.0, 1.0));
return output;
}
`,
fragment: `
[[stage(fragment)]]
fn main([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return fragPosition;
}
`
}
};
const UNIFORM_BUFFER_SIZE = 4 * 16; // 4x4 matrix
const CUBE_ATTRIBUTE_LAYOUTS: AttributeBinding[] = [
{name: 'position', location: 0, accessor: {format: 'float32x4'}},
{name: 'uv', location: 1, accessor: {format: 'float32x2'}}
];
const CUBE_RENDER_PARAMETERS: RenderPipelineParameters = {
// Enable depth testing so that the fragment closest to the camera
// is rendered in front.
depthWriteEnabled: true,
depthCompare: 'less',
depthFormat: 'depth24plus',
// 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',
};
export async function init(canvas: HTMLCanvasElement, language: 'glsl' | 'wgsl') {
const device = await WebGPUDevice.create({canvas});
// Create a vertex buffer from the cube data.
const positionBuffer = device.createBuffer({id: 'cube-positions', data: cubePositions});
const uvBuffer = device.createBuffer({id: 'cube-uvs', data: cubeUVs});
const uniformBuffer1 = device.createBuffer({
id: 'uniforms-1',
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
byteLength: UNIFORM_BUFFER_SIZE,
});
const cubeModel1 = new Model(device, {
id: 'cube',
vs: SHADERS[language].vertex,
fs: SHADERS[language].fragment,
topology: 'triangle-list',
attributeLayouts: CUBE_ATTRIBUTE_LAYOUTS,
attributeBuffers: [positionBuffer, uvBuffer],
bindings: [uniformBuffer1],
vertexCount: cubeVertexCount,
parameters: CUBE_RENDER_PARAMETERS
});
const uniformBuffer2 = device.createBuffer({
id: 'uniforms-1',
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
byteLength: UNIFORM_BUFFER_SIZE,
});
const cubeModel2 = new Model(device, {
id: 'cube',
vs: SHADERS[language].vertex,
fs: SHADERS[language].fragment,
topology: 'triangle-list',
attributeLayouts: CUBE_ATTRIBUTE_LAYOUTS,
attributeBuffers: [positionBuffer, uvBuffer],
bindings: [uniformBuffer2],
vertexCount: cubeVertexCount,
parameters: CUBE_RENDER_PARAMETERS
});
const projectionMatrix = new Matrix4();
const viewMatrix = new Matrix4();
const modelViewProjectionMatrix = new Matrix4();
function frame() {
const aspect = canvas.width / canvas.height;
const now = Date.now() / 1000;
projectionMatrix.perspective({fov: (2 * Math.PI) / 5, aspect, near: 1, far: 100.0});
viewMatrix.identity().translate([-2, 0, -7]).rotateAxis(1, [Math.sin(now), Math.cos(now), 0]);
modelViewProjectionMatrix.copy(viewMatrix).multiplyLeft(projectionMatrix);
uniformBuffer1.write(new Float32Array(modelViewProjectionMatrix));
viewMatrix.identity().translate([2, 0, -7]).rotateAxis(1, [Math.cos(now), Math.sin(now), 0]);
modelViewProjectionMatrix.copy(viewMatrix).multiplyLeft(projectionMatrix);
uniformBuffer2.write(new Float32Array(modelViewProjectionMatrix));
device.beginRenderPass();
cubeModel1.draw();
cubeModel2.draw();
device.submit();
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
init(document.getElementById('canvas') as HTMLCanvasElement, 'wgsl');

View File

@ -0,0 +1,142 @@
export const cubeVertexSize = 4 * 10; // Byte size of one cube vertex.
export const cubePositionOffset = 0;
export const cubeColorOffset = 4 * 4; // Byte offset of cube vertex color attribute.
export const cubeUVOffset = 4 * 8;
export const cubeVertexCount = 36;
// float4 position
// prettier-ignore
export const cubePositions = new Float32Array([
1, -1, 1, 1,
-1, -1, 1, 1,
-1, -1, -1, 1,
1, -1, -1, 1,
1, -1, 1, 1,
-1, -1, -1, 1,
1, 1, 1, 1,
1, -1, 1, 1,
1, -1, -1, 1,
1, 1, -1, 1,
1, 1, 1, 1,
1, -1, -1, 1,
-1, 1, 1, 1,
1, 1, 1, 1,
1, 1, -1, 1,
-1, 1, -1, 1,
-1, 1, 1, 1,
1, 1, -1, 1,
-1, -1, 1, 1,
-1, 1, 1, 1,
-1, 1, -1, 1,
-1, -1, -1, 1,
-1, -1, 1, 1,
-1, 1, -1, 1,
1, 1, 1, 1,
-1, 1, 1, 1,
-1, -1, 1, 1,
-1, -1, 1, 1,
1, -1, 1, 1,
1, 1, 1, 1,
1, -1, -1, 1,
-1, -1, -1, 1,
-1, 1, -1, 1,
1, 1, -1, 1,
1, -1, -1, 1,
-1, 1, -1, 1,
]);
// float2 uv,
// prettier-ignore
export const cubeUVs = new Float32Array([
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 0,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 0,
1, 1,
0, 1,
0, 0,
0, 0,
1, 0,
1, 1,
1, 1,
0, 1,
0, 0,
1, 0,
1, 1,
0, 0,
]);
export const cubeColors = new Float32Array([
// float4 position, float4 color, float2 uv,
1, 0, 1, 1,
0, 0, 1, 1,
0, 0, 0, 1,
1, 0, 0, 1,
1, 0, 1, 1,
0, 0, 0, 1,
1, 1, 1, 1,
1, 0, 1, 1,
1, 0, 0, 1,
1, 1, 0, 1,
1, 1, 1, 1,
1, 0, 0, 1,
0, 1, 1, 1,
1, 1, 1, 1,
1, 1, 0, 1,
0, 1, 0, 1,
0, 1, 1, 1,
1, 1, 0, 1,
0, 0, 1, 1,
0, 1, 1, 1,
0, 1, 0, 1,
0, 0, 0, 1,
0, 0, 1, 1,
0, 1, 0, 1,
1, 1, 1, 1,
0, 1, 1, 1,
0, 0, 1, 1,
0, 0, 1, 1,
1, 0, 1, 1,
1, 1, 1, 1,
1, 0, 0, 1,
0, 0, 0, 1,
0, 1, 0, 1,
1, 1, 0, 1,
1, 0, 0, 1,
0, 1, 0, 1,
]);

View File

@ -0,0 +1,7 @@
<!doctype html>
<script type="module">
import './app.ts';
</script>
<body>
<canvas id="canvas"></canvas>
</body>

View File

@ -0,0 +1,20 @@
{
"name": "luma.gl-examples-webgpu-two-cubes",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "vite",
"build": "tsc && vite build",
"serve": "vite preview"
},
"dependencies": {
"@luma.gl/engine": "8.6.0-alpha.5",
"@luma.gl/shadertools": "8.6.0-alpha.5",
"@luma.gl/webgpu": "8.6.0-alpha.5",
"@math.gl/core": "^3.5.0"
},
"devDependencies": {
"typescript": "^4.3.2",
"vite": "^2.6.4"
}
}

View File

@ -0,0 +1,18 @@
import fs from 'fs/promises';
import { defineConfig } from 'vite'
/** @see https://vitejs.dev/config/ */
export default defineConfig(async () => ({
resolve: {alias: await getAliases('@luma.gl', `${__dirname}/../../..`)},
server: {open: true}
}));
/** Run against local source */
const getAliases = async (frameworkName, frameworkRootDir) => {
const modules = await fs.readdir(`${frameworkRootDir}/modules`)
const aliases = {}
for (const module of modules) {
aliases[`${frameworkName}/${module}`] = `${frameworkRootDir}/modules/${module}/src`
}
return aliases
}