<!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>
rotationx:<input type="range" min="0" max="360" step="0.1" name="rotationx" value="0" /><br>
rotationy:<input type="range" min="0" max="360" step="0.1" name="rotationy" value="0" /><br>
rotationz:<input type="range" min="0" max="360" step="0.1" name="rotationz" 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 createFVertices() {
const positions = [
// left column
0, 0, 0,
30, 0, 0,
0, 150, 0,
30, 150, 0,
// top rung
30, 0, 0,
100, 0, 0,
30, 30, 0,
100, 30, 0,
// middle rung
30, 60, 0,
70, 60, 0,
30, 90, 0,
70, 90, 0,
// left column back
0, 0, 30,
30, 0, 30,
0, 150, 30,
30, 150, 30,
// top rung back
30, 0, 30,
100, 0, 30,
30, 30, 30,
100, 30, 30,
// middle rung back
30, 60, 30,
70, 60, 30,
30, 90, 30,
70, 90, 30,
];
const indices = [
// front
0, 1, 2, 2, 1, 3, // left column
4, 5, 6, 6, 5, 7, // top rung
8, 9, 10, 10, 9, 11, // middle rung
// back
12, 13, 14, 14, 13, 15, // left column back
16, 17, 18, 18, 17, 19, // top rung back
20, 21, 22, 22, 21, 23, // middle rung back
0, 5, 12, 12, 5, 17, // top
5, 7, 17, 17, 7, 19, // top rung right
6, 7, 18, 18, 7, 19, // top rung bottom
6, 8, 18, 18, 8, 20, // between top and middle rung
8, 9, 20, 20, 9, 21, // middle rung top
9, 11, 21, 21, 11, 23, // middle rung right
10, 11, 22, 22, 11, 23, // middle rung bottom
10, 3, 22, 22, 3, 15, // stem right
2, 3, 14, 14, 3, 15, // bottom
0, 2, 12, 12, 2, 14, // left
];
const quadColors = [
200, 70, 120, // left column front
200, 70, 120, // top rung front
200, 70, 120, // middle rung front
80, 70, 200, // left column back
80, 70, 200, // top rung back
80, 70, 200, // middle rung back
70, 200, 210, // top
160, 160, 220, // top rung right
90, 130, 110, // top rung bottom
200, 200, 70, // between top and middle rung
210, 100, 70, // middle rung top
210, 160, 70, // middle rung right
70, 180, 210, // middle rung bottom
100, 70, 210, // stem right
76, 210, 100, // bottom
140, 210, 80, // left
];
const numVertices = indices.length;
const vertexData = new Float32Array(numVertices * 4); // xyz + color
vertexData.fill(-1);
const colorData = new Uint8Array(vertexData.buffer);
for (let i = 0; i < indices.length; ++i) {
const positionNdx = indices[i] * 3;
const position = positions.slice(positionNdx, positionNdx + 3);
vertexData.set(position, i * 4);
const quadNdx = (i / 6 | 0) * 3;
const color = quadColors.slice(quadNdx, quadNdx + 3);
colorData.set(color, i * 16 + 12);
colorData[i * 16 + 15] = 255;
}
return {
vertexData,
numVertices,
};
}
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 {
matrix: mat4x4f,
};
struct Vertex {
@location(0) position: vec4f,
@location(1) color: vec4f,
};
struct VSOutput {
@builtin(position) position: vec4f,
@location(0) color: vec4f,
};
@group(0) @binding(0) var<uniform> uni: Uniforms;
@vertex fn vs(vert: Vertex) -> VSOutput {
var vsOut: VSOutput;
vsOut.position = uni.matrix * vert.position;
vsOut.color = vert.color;
return vsOut;
}
@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f {
return vsOut.color;
}
`
});
const pipeline = device.createRenderPipeline({
label: 'just 2d position',
layout: 'auto',
vertex: {
module,
entryPoint: 'vs',
buffers: [
{
arrayStride: (4) * 4, // (2) floats, 4 bytes each
attributes: [
{ shaderLocation: 0, offset: 0, format: 'float32x3' }, // position
{shaderLocation: 1, offset: 12, format: 'unorm8x4'}, // 顏色
],
},
],
},
primitive: {
cullMode: 'back',//front
},
fragment: {
module,
entryPoint: 'fs',
targets: [{ format: presentationFormat }],
},
depthStencil: {
depthWriteEnabled: true,
depthCompare: 'less',
format: 'depth24plus',
},
});
// color, resolution, padding, matrix
const uniformBufferSize = (16) * 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 kMatrixOffset = 0;
// const colorValue = uniformValues.subarray(kColorOffset, kColorOffset + 4);
const matrixValue = uniformValues.subarray(kMatrixOffset, kMatrixOffset + 16);
// 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 } = createFVertices();
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',
},
],
depthStencilAttachment: {
// view: <- 渲染時填寫
depthClearValue: 1.0,
depthLoadOp: 'clear',
depthStoreOp: 'store',
},
};
let depthTexture;
function render() {
// Get the current texture from the canvas context and
// set it as the texture to render to.
const canvasTexture = context.getCurrentTexture();
renderPassDescriptor.colorAttachments[0].view = canvasTexture.createView();
// If we don't have a depth texture OR if its size is different
// from the canvasTexture when make a new depth texture
if (!depthTexture ||
depthTexture.width !== canvasTexture.width ||
depthTexture.height !== canvasTexture.height) {
if (depthTexture) {
depthTexture.destroy();
}
depthTexture = device.createTexture({
size: [canvasTexture.width, canvasTexture.height],
format: 'depth24plus',
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
}
renderPassDescriptor.depthStencilAttachment.view = depthTexture.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 rotationx = document
.querySelector('input[name="rotationx"]')
.value;
rotationx = rotationx * Math.PI / 180;
let rotationy = document
.querySelector('input[name="rotationy"]')
.value;
rotationy = rotationy * Math.PI / 180;
let rotationz = document
.querySelector('input[name="rotationz"]')
.value;
rotationz = rotationz * Math.PI / 180;
let scalex = document
.querySelector('input[name="scalex"]')
.value;
let scaley = document
.querySelector('input[name="scaley"]')
.value;
let translationMatrix = glMatrix.mat4.create();
glMatrix.mat4.translate(translationMatrix, translationMatrix, glMatrix.vec3 .fromValues(translationx, translationy,1));
let rotationMatrix = glMatrix.mat4.create();
// glMatrix.mat4.rotate(rotationMatrix, rotationMatrix, 1, glMatrix.vec3.fromValues(Math.sin(rotation), Math.cos(rotation), 0));
//glMatrix.mat4.rotate(rotationMatrix, rotationMatrix, rotation);
glMatrix.mat4.rotateX(rotationMatrix, rotationMatrix, rotationx);
glMatrix.mat4.rotateY(rotationMatrix, rotationMatrix, rotationy);
glMatrix.mat4.rotateZ(rotationMatrix, rotationMatrix, rotationz);
let scaleMatrix = glMatrix.mat4.create();
glMatrix.mat4.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.mat4.create();
//glMatrix.mat4.translate(moveOriginMatrix, moveOriginMatrix, glMatrix.vec2.fromValues(-50, -70));
//-------------
//固定相机,正交投影
// const aspect = Math.abs(canvas.width / canvas.height);
let projectionMatrix = glMatrix.mat4.create();
// glMatrix.mat4.perspective(projectionMatrix, (2 * Math.PI) / 5, aspect, 1, 100.0);
// glMatrix.mat4.projection(projectionMatrix,400,canvas.clientWidth, canvas.clientHeight);
glMatrix.mat4.ortho(projectionMatrix,0,canvas.clientWidth, 0,canvas.clientHeight,400,-400);
/*
mat4.ortho(
0, // 左边
canvas.clientWidth, // 正确的
canvas.clientHeight, // 底部
0, // 顶部
400, // 靠近
-400, // 远的
matrixValue, // 夏令时
);
*/
//-------------
let matrix = glMatrix.mat4.create();
//glMatrix.mat4.multiply(matrix, translationMatrix, rotationMatrix);
glMatrix.mat4.multiply(matrix,projectionMatrix, translationMatrix);
glMatrix.mat4.multiply(matrix, matrix, rotationMatrix);
glMatrix.mat4.multiply(matrix, matrix, scaleMatrix);
// matrix = mat3.multiply(matrix, moveOriginMatrix);
glMatrix.mat4.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,
]);
*/
//console.log(matrix)
matrixValue.set(matrix);
// upload the uniform values to the uniform buffer
device.queue.writeBuffer(uniformBuffer, 0, uniformValues);
pass.setBindGroup(0, bindGroup);
// pass.drawIndexed(numVertices);
pass.draw(numVertices);
//pass.drawIndexed(numVertices);
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="rotationx"]')
.addEventListener("input", (e) => {
render();
});
document
.querySelector('input[name="rotationy"]')
.addEventListener("input", (e) => {
render();
});
document
.querySelector('input[name="rotationz"]')
.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>