<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>てst</title>
</head>
<body>
<!-- canvas:用来展示WebGPU渲染的结果 -->
<canvas id="webgpu" width="500" height="500"></canvas>
translationx:<input type="range" min="0" max="500" step="0.1" name="translationx" value="230.2" /><br>
translationy:<input type="range" min="0" max="500" step="0.1" name="translationy" value="230.2" /><br>
rotation:<input type="range" min="0" max="360" step="0.1" name="rotation" value="0" /><br>
scalex:<input type="range" min="-5" max="5" step="0.1" name="scalex" value="1.1" /><br>
scaley:<input type="range" min="-5" max="5" step="0.1" name="scaley" value="1.1" /><br>
<script type="module">
//"C:\Program Files\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="D:\angular\webgpu\n" --disable-site-isolation-trials
import * as glMatrix from './dist/esm/index.js';
function createTranVertices() {
const vertexData = new Float32Array([
100.0, 50, 0.0,
50, 50, 0.0,
50, 50, 0.0
]);
return {
vertexData,
numVertices: 3,
};
}
async function main() {
const adapter = await navigator.gpu?.requestAdapter();
const device = await adapter?.requestDevice();
if (!device) {
fail('need a browser that supports WebGPU');
return;
}
// Get a WebGPU context from the canvas and configure it
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
alphaMode: 'premultiplied',
});
const module = device.createShaderModule({
code: `
struct Uniforms {
color: vec4f,
resolution: vec2f,
matrix: mat3x3f,
};
struct Vertex {
@location(0) position: vec2f,
};
struct VSOutput {
@builtin(position) position: vec4f,
};
@group(0) @binding(0) var<uniform> uni: Uniforms;
@vertex fn vs(vert: Vertex) -> VSOutput {
var vsOut: VSOutput;
// Multiply by a matrix
let position = (uni.matrix * vec3f(vert.position, 1)).xy;
// convert the position from pixels to a 0.0 to 1.0 value
let zeroToOne = position / uni.resolution;
// convert from 0 <-> 1 to 0 <-> 2
let zeroToTwo = zeroToOne * 2.0;
// covert from 0 <-> 2 to -1 <-> +1 (clip space)
let flippedClipSpace = zeroToTwo - 1.0;
// flip Y
let clipSpace = flippedClipSpace * vec2f(1, -1);
vsOut.position = vec4f(clipSpace, 0.0, 1.0);
return vsOut;
}
@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f {
return uni.color;
}
`
});
const pipeline = device.createRenderPipeline({
label: 'just 2d position',
layout: 'auto',
vertex: {
module,
entryPoint: 'vs',
buffers: [
{
arrayStride: (2) * 4, // (2) floats, 4 bytes each
attributes: [
{ shaderLocation: 0, offset: 0, format: 'float32x2' }, // position
],
},
],
},
fragment: {
module,
entryPoint: 'fs',
targets: [{ format: presentationFormat }],
},
});
// color, resolution, padding, matrix
const uniformBufferSize = (4 + 2 + 2 + 12) * 4;
const uniformBuffer = device.createBuffer({
label: 'uniforms',
size: uniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const uniformValues = new Float32Array(uniformBufferSize / 4);
// offsets to the various uniform values in float32 indices
const kColorOffset = 0;
const kResolutionOffset = 4;
const kMatrixOffset = 8;
const colorValue = uniformValues.subarray(kColorOffset, kColorOffset + 4);
const resolutionValue = uniformValues.subarray(kResolutionOffset, kResolutionOffset + 2);
const matrixValue = uniformValues.subarray(kMatrixOffset, kMatrixOffset + 12);
// The color will not change so let's set it once at init time
colorValue.set([Math.random(), Math.random(), Math.random(), 1]);
const { vertexData, numVertices } = createTranVertices();
const vertexBuffer = device.createBuffer({
label: 'vertex buffer vertices',
size: vertexData.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(vertexBuffer, 0, vertexData);
const bindGroup = device.createBindGroup({
label: 'bind group for object',
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: uniformBuffer } },
],
});
const renderPassDescriptor = {
label: 'our basic canvas renderPass',
colorAttachments: [
{
// view: <- to be filled out when we render
loadOp: 'clear',
storeOp: 'store',
},
],
};
function render() {
// Get the current texture from the canvas context and
// set it as the texture to render to.
renderPassDescriptor.colorAttachments[0].view =
context.getCurrentTexture().createView();
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass(renderPassDescriptor);
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, vertexBuffer);
let translationx = document
.querySelector('input[name="translationx"]')
.value;
let translationy = document
.querySelector('input[name="translationy"]')
.value;
let rotation = document
.querySelector('input[name="rotation"]')
.value;
rotation = rotation * Math.PI / 180;
let scalex = document
.querySelector('input[name="scalex"]')
.value;
let scaley = document
.querySelector('input[name="scaley"]')
.value;
let translationMatrix = glMatrix.mat3.create();
glMatrix.mat3.translate(translationMatrix, translationMatrix, glMatrix.vec2.fromValues(translationx, translationy));
let rotationMatrix = glMatrix.mat3.create();
//glMatrix.mat3.rotate(rotationMatrix, rotationMatrix, 1, glMatrix.vec3.fromValues(Math.sin(rotation), Math.cos(rotation), 0));
glMatrix.mat3.rotate(rotationMatrix, rotationMatrix, rotation);
let scaleMatrix = glMatrix.mat3.create();
glMatrix.mat3.scale(scaleMatrix, scaleMatrix, [scalex, scaley, 1]);
// make a matrix that will move the origin of the 'F' to its center.
// const moveOriginMatrix = mat3.translation([-50, -75]);
let moveOriginMatrix = glMatrix.mat3.create();
glMatrix.mat3.translate(moveOriginMatrix, moveOriginMatrix, glMatrix.vec2.fromValues(-50, -70));
let matrix = glMatrix.mat3.create();
glMatrix.mat3.multiply(matrix, translationMatrix, rotationMatrix);
glMatrix.mat3.multiply(matrix, matrix, scaleMatrix);
// matrix = mat3.multiply(matrix, moveOriginMatrix);
glMatrix.mat3.multiply(matrix, matrix, moveOriginMatrix);
console.log('matrix:' + matrix);
console.log('matrix0-3:' + matrix.slice(0, 3));
console.log('matrixValue:' + matrixValue);
// Set the uniform values in our JavaScript side Float32Array
resolutionValue.set([canvas.width, canvas.height]);
matrixValue.set([
...matrix.slice(0, 3), 0,
...matrix.slice(3, 6), 0,
...matrix.slice(6, 9), 0,
]);
// upload the uniform values to the uniform buffer
device.queue.writeBuffer(uniformBuffer, 0, uniformValues);
pass.setBindGroup(0, bindGroup);
// pass.drawIndexed(numVertices);
pass.draw(3);
pass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
}
//-----------------
document
.querySelector('input[name="translationx"]')
.addEventListener("input", (e) => {
render();
});
document
.querySelector('input[name="translationy"]')
.addEventListener("input", (e) => {
render();
});
document
.querySelector('input[name="rotation"]')
.addEventListener("input", (e) => {
render();
});
document
.querySelector('input[name="scalex"]')
.addEventListener("input", (e) => {
render();
});
document
.querySelector('input[name="scaley"]')
.addEventListener("input", (e) => {
render();
});
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const canvas = entry.target;
const width = entry.contentBoxSize[0].inlineSize;
const height = entry.contentBoxSize[0].blockSize;
canvas.width = Math.max(1, Math.min(width, device.limits.maxTextureDimension2D));
canvas.height = Math.max(1, Math.min(height, device.limits.maxTextureDimension2D));
// re-render
render();
}
});
observer.observe(canvas);
}
function fail(msg) {
alert(msg);
}
main();
</script>
</body>
</html>