- 引入gl-matrix矩阵库
npm install gl-matrix -S
- index.html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="webgpu" width="500" height="500" style="background-color: black;"></canvas>
<script type="module">
import { vertex,fragment } from "./shader.js";
import * as glMatrix from './node_modules/gl-matrix/esm/index.js';
if(navigator.gpu) {
console.log('浏览器支持WebGPU');
}else{
console.log('浏览器不支持webGPU');
}
//创建虚拟GPU设备对象
const adapter = await navigator.gpu.requestAdapter()
const device = await adapter.requestDevice()
//创建canvas用于承载webgpu渲染的画布
const canvas = document.getElementById('webgpu')
//获取webgpu渲染的上下文对象
const context = canvas.getContext('webgpu')
//获取浏览器颜色格式
const format = navigator.gpu.getPreferredCanvasFormat()
//将webgpu对象与画布上下文关联起来 完成相关配置
context.configure({
device: device,
format: format//获取浏览器颜色格式
})
//使用类型化数组定义图像坐标点 创建顶点缓冲区表示顶点数据
const vertexArray = new Float32Array([
//三角形三个顶点坐标的x,y,z值
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 0.0,
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
1.0, 1.0, 0.0,
])
//当device.createBuffer执行时,会在电脑显卡GPU内存中开辟一片存储空间用来存储顶点数据
//创建顶点缓冲区
const vertexBuffer = device.createBuffer({
size: vertexArray.byteLength,//缓冲区长度
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
})
//将顶点数据写入顶点缓冲区
device.queue.writeBuffer(vertexBuffer,0,vertexArray)
//uniform数据:缩放矩阵 浮点数
const mat4Array = new Float32Array([0.5,0,0,0,0,0.5,0,0,0,0,1,0,0,0,0,1])
const mat4Buffer = device.createBuffer({
size: mat4Array.byteLength,//缓冲区长度
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
})
device.queue.writeBuffer(mat4Buffer,0,mat4Array)
const mat4T1 = glMatrix.mat4.create()
glMatrix.mat4.translate(mat4T1,mat4T1,[-1,0,0])
// const mat4TArray = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,-1,-1,0,1])
const mat4TArray = mat4T1
const mat4TBuffer = device.createBuffer({
size: mat4TArray.byteLength,//缓冲区长度
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
})
device.queue.writeBuffer(mat4TBuffer,0,mat4TArray)
const t = 0.5
const tArray = new Float32Array([t])
const tBuffer = device.createBuffer({
size: tArray.byteLength,//缓冲区长度
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
})
device.queue.writeBuffer(tBuffer, 0, tArray)
const colorArray = new Float32Array([1.0,1.0,0.0])
const colorBuffer = device.createBuffer({
size: colorArray.byteLength,//缓冲区长度
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
})
device.queue.writeBuffer(colorBuffer,0,colorArray)
//创建一个webgpu渲染管线对象pipeline
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {//顶点相关属性配置
module: device.createShaderModule({ code: vertex}),
entryPoint: "main",
buffers: [
{
arrayStride: 3 * 4,//一个顶点数据占用的字节长度
attributes: [{
shaderLocation: 0, //标记顶点缓冲区为0,也可以设置为其它值
format: "float32x3", //一个顶点有3个float32
offset: 0
}]
}
]
},
fragment: {//片元着色器引入
module: device.createShaderModule({ code: fragment}),
entryPoint: "main",
targets: [{
format: format
}]
},
primitive: {
//绘制三角形、线条、点
topology: "triangle-list",//绘制三角形
},
});
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
// {
// binding: 0,
// resource: {buffer: mat4Buffer}
// // resource: {buffer: tBuffer}
// },
{
binding: 1,
// resource: {buffer: mat4Buffer}
resource: {buffer: colorBuffer}
},
{
binding: 0,
resource: {buffer: mat4TBuffer}
}
]
})
// WGSL着色器语言
//创建命令编码器对象
const commandEncoder = device.createCommandEncoder();
//创建一个渲染通道
const renderPass = commandEncoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),//结果输出到canvas画布上,颜色缓冲区
storeOp: 'store',
loadOp: 'clear',
clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0},//渲染的背景颜色
}]
})
//给渲染通道设置渲染管线,可设置多条渲染管线
renderPass.setPipeline(pipeline);
//设置顶点数据缓冲区,即上述定义标记为0的缓冲区,也可以为其它
renderPass.setVertexBuffer(0, vertexBuffer);
renderPass.setBindGroup(0, bindGroup);
//绘制命令
renderPass.draw(3);
//绘制完结束绘制
renderPass.end()
//将生成的GPU命令存入GPU命令缓冲区
const commandBuffer = commandEncoder.finish()
device.queue.submit([commandBuffer])
//glMatrix.mat4.fromValues创建矩阵,一列列写
// const mat4T = glMatrix.mat4.fromValues(1,0,0,0, 0,1,0,0, 0,0,1,0, 1,2,3,1)
// const mat4S = glMatrix.mat4.fromValues(1,0,0,0,0,2,0,0,0,0,3,0,0,0,0,1)
//创建一个单位矩阵
const mat4 = glMatrix.mat4.create()
//创建一个平移矩阵 沿着x轴平移2, 生成的mat4T即为平移矩阵
const mat4T = glMatrix.mat4.create()
glMatrix.mat4.translate(mat4T,mat4,[2,0,0])
//生成缩放矩阵 x缩放10
const mat4S = glMatrix.mat4.create()
glMatrix.mat4.scale(mat4S,mat4,[10,1,1])
//生成绕x轴旋转45度旋转矩阵
const mat4X = glMatrix.mat4.create()
glMatrix.mat4.rotateX(mat4X,mat4,Math.PI/4)
//生成绕y轴旋转45度旋转矩阵
const mat4Y = glMatrix.mat4.create()
glMatrix.mat4.rotateY(mat4Y,mat4,Math.PI/4)
//生成绕Z轴旋转45度旋转矩阵
const mat4Z = glMatrix.mat4.create()
glMatrix.mat4.rotateZ(mat4Z,mat4,Math.PI/4)
//矩阵乘法运算 先平移2,再缩放10 生成模型矩阵 方法1
// const modelMatrix = glMatrix.mat4.create()
// glMatrix.mat4.multiply(modelMatrix,modelMatrix,mat4S) //后缩放
// glMatrix.mat4.multiply(modelMatrix,modelMatrix,mat4T) //先平移
//生成模型矩阵 方法2
const modelMatrix = glMatrix.mat4.create()
glMatrix.mat4.scale(modelMatrix,modelMatrix,[10,1,1])
glMatrix.mat4.translate(modelMatrix,modelMatrix,[2,0,0])
console.log(modelMatrix);
//创建3维向量表示顶点坐标,对顶点p1进行变换,变换后的值存在p2中
const p1 = glMatrix.vec3.fromValues(2,0,0)
const p2 = glMatrix.vec3.create()
glMatrix.vec3.transformMat4(p2,p1,modelMatrix)
</script>
</body>
</html>
- shader.js代码 WGSL着色器语言
const vertex = /*wgsl*/ `
// @group(0) @binding(0) var<uniform> t:f32;
@group(0) @binding(0) var<uniform> S:mat4x4<f32>;
// @group(0) @binding(2) var<uniform> T:mat4x4<f32>;
@vertex
fn main(@location(0) pos: vec3<f32>)-> @builtin(position) vec4<f32>{//@location(0)表示GPU显存中标记为0的顶点缓冲区顶点数据,如果在顶点缓冲区标记为1,则写1
var pos2 = vec4<f32>(pos,1.0);//三维向量转成4维齐次向量
// pos2.x -= 0.2;//所有顶点的x坐标偏移0.2
//将所有顶点坐标缩放
// 0.5 0 0 0
// 0 0.5 0 0
// 0 0 1 0
// 0 0 0 1
// var mat = mat4x4<f32>(0.5,0,0,0,0,0.5,0,0,0,0,1,0,0,0,0,1);
//平移-1 -1
// var mat = mat4x4<f32>(1,0,0,0,0,1,0,0,0,0,1,0,-1,-1,0,1);
// pos2 = mat * pos2;
// var s = mat4x4<f32>(t,0,0,0,0,t,0,0,0,0,1,0,0,0,0,1);
// pos2 = s * pos2;
//先缩放后平移
// pos2 = T * S * pos2;
pos2 = S * pos2;
return pos2;
}
`
const fragment = /*wgsl*/ `
@group(0) @binding(1) var<uniform> color:vec3<f32>;
@fragment
fn main() -> @location(0) vec4<f32>{
// return vec4<f32>(1.0,0.0,0.0,1.0); //红色片元
return vec4<f32>(color,1.0); //红色片元
}
`
export {
vertex,
fragment
}
旋转矩形
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="webgpu" width="500" height="500" style="background-color: black;"></canvas>
<script type="module">
import { vertex,fragment } from "./shader.js";
import * as glMatrix from './node_modules/gl-matrix/esm/index.js';
if(navigator.gpu) {
console.log('浏览器支持WebGPU');
}else{
console.log('浏览器不支持webGPU');
}
//创建虚拟GPU设备对象
const adapter = await navigator.gpu.requestAdapter()
const device = await adapter.requestDevice()
//创建canvas用于承载webgpu渲染的画布
const canvas = document.getElementById('webgpu')
//获取webgpu渲染的上下文对象
const context = canvas.getContext('webgpu')
//获取浏览器颜色格式
const format = navigator.gpu.getPreferredCanvasFormat()
//将webgpu对象与画布上下文关联起来 完成相关配置
context.configure({
device: device,
format: format//获取浏览器颜色格式
})
//使用类型化数组定义图像坐标点 创建顶点缓冲区表示顶点数据
const vertexArray = new Float32Array([
//三角形三个顶点坐标的x,y,z值
1.0, 0.5, 0.0,
-1.0, 0.5, 0.0,
-1.0, -0.5, 0.0,
1.0, 0.5, 0.0,
-1.0, -0.5, 0.0,
1.0, -0.5, 0.0,
])
//当device.createBuffer执行时,会在电脑显卡GPU内存中开辟一片存储空间用来存储顶点数据
//创建顶点缓冲区
const vertexBuffer = device.createBuffer({
size: vertexArray.byteLength,//缓冲区长度
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
})
//将顶点数据写入顶点缓冲区
device.queue.writeBuffer(vertexBuffer,0,vertexArray)
// const mat4TArray = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,-1,-1,0,1])
const mat4T1 = glMatrix.mat4.create()
// glMatrix.mat4.translate(mat4T1,mat4T1,[-1,0,0])
glMatrix.mat4.rotateZ(mat4T1,mat4T1,Math.PI/2)
const mat4TArray = mat4T1
const mat4TBuffer = device.createBuffer({
size: mat4TArray.byteLength,//缓冲区长度
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
})
device.queue.writeBuffer(mat4TBuffer,0,mat4TArray)
const colorArray = new Float32Array([1.0,1.0,0.0])
const colorBuffer = device.createBuffer({
size: colorArray.byteLength,//缓冲区长度
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST//缓冲区用途:用于存储顶点数据
})
device.queue.writeBuffer(colorBuffer,0,colorArray)
//创建一个webgpu渲染管线对象pipeline
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {//顶点相关属性配置
module: device.createShaderModule({ code: vertex}),
entryPoint: "main",
buffers: [
{
arrayStride: 3 * 4,//一个顶点数据占用的字节长度
attributes: [{
shaderLocation: 0, //标记顶点缓冲区为0,也可以设置为其它值
format: "float32x3", //一个顶点有3个float32
offset: 0
}]
}
]
},
fragment: {//片元着色器引入
module: device.createShaderModule({ code: fragment}),
entryPoint: "main",
targets: [{
format: format
}]
},
primitive: {
//绘制三角形、线条、点
topology: "triangle-list",//绘制三角形
},
});
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
// resource: {buffer: mat4Buffer}
resource: {buffer: mat4TBuffer}
// resource: {buffer: tBuffer}
},
{
binding: 1,
// resource: {buffer: mat4Buffer}
resource: {buffer: colorBuffer}
},
// {
// binding: 2,
// resource: {buffer: mat4TBuffer}
// },
]
})
// WGSL着色器语言
// //创建命令编码器对象
// const commandEncoder = device.createCommandEncoder();
// //创建一个渲染通道
// const renderPass = commandEncoder.beginRenderPass({
// colorAttachments: [{
// view: context.getCurrentTexture().createView(),//结果输出到canvas画布上,颜色缓冲区
// storeOp: 'store',
// loadOp: 'clear',
// clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0},//渲染的背景颜色
// }]
// })
// //给渲染通道设置渲染管线,可设置多条渲染管线
// renderPass.setPipeline(pipeline);
// //设置顶点数据缓冲区,即上述定义标记为0的缓冲区,也可以为其它
// renderPass.setVertexBuffer(0, vertexBuffer);
// renderPass.setBindGroup(0, bindGroup);
// //绘制命令
// renderPass.draw(6);
// //绘制完结束绘制
// renderPass.end()
// //将生成的GPU命令存入GPU命令缓冲区
// const commandBuffer = commandEncoder.finish()
// device.queue.submit([commandBuffer])
//glMatrix.mat4.fromValues创建矩阵,一列列写
// const mat4T = glMatrix.mat4.fromValues(1,0,0,0, 0,1,0,0, 0,0,1,0, 1,2,3,1)
// const mat4S = glMatrix.mat4.fromValues(1,0,0,0,0,2,0,0,0,0,3,0,0,0,0,1)
//创建一个单位矩阵
// const mat4 = glMatrix.mat4.create()
// //创建一个平移矩阵 沿着x轴平移2, 生成的mat4T即为平移矩阵
// const mat4T = glMatrix.mat4.create()
// glMatrix.mat4.translate(mat4T,mat4,[2,0,0])
// //生成缩放矩阵 x缩放10
// const mat4S = glMatrix.mat4.create()
// glMatrix.mat4.scale(mat4S,mat4,[10,1,1])
// //生成绕x轴旋转45度旋转矩阵
// const mat4X = glMatrix.mat4.create()
// glMatrix.mat4.rotateX(mat4X,mat4,Math.PI/4)
// //生成绕y轴旋转45度旋转矩阵
// const mat4Y = glMatrix.mat4.create()
// glMatrix.mat4.rotateY(mat4Y,mat4,Math.PI/4)
// //生成绕Z轴旋转45度旋转矩阵
// const mat4Z = glMatrix.mat4.create()
// glMatrix.mat4.rotateZ(mat4Z,mat4,Math.PI/4)
// //矩阵乘法运算 先平移2,再缩放10 生成模型矩阵 方法1
// // const modelMatrix = glMatrix.mat4.create()
// // glMatrix.mat4.multiply(modelMatrix,modelMatrix,mat4S) //后缩放
// // glMatrix.mat4.multiply(modelMatrix,modelMatrix,mat4T) //先平移
// //生成模型矩阵 方法2
// const modelMatrix = glMatrix.mat4.create()
// glMatrix.mat4.scale(modelMatrix,modelMatrix,[10,1,1])
// glMatrix.mat4.translate(modelMatrix,modelMatrix,[2,0,0])
// console.log(modelMatrix);
// //创建3维向量表示顶点坐标,对顶点p1进行变换,变换后的值存在p2中
// const p1 = glMatrix.vec3.fromValues(2,0,0)
// const p2 = glMatrix.vec3.create()
// glMatrix.vec3.transformMat4(p2,p1,modelMatrix)
// console.log(p1,p2);
let angle = 0.0;
function render(){
angle+=0.05;
const mat4T1 = glMatrix.mat4.create()
// glMatrix.mat4.translate(mat4T1,mat4T1,[-1,0,0])
glMatrix.mat4.rotateZ(mat4T1,mat4T1,angle)
console.log(mat4T1);
device.queue.writeBuffer(mat4TBuffer,0,mat4T1)
//创建命令编码器对象
const commandEncoder = device.createCommandEncoder();
//创建一个渲染通道
const renderPass = commandEncoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),//结果输出到canvas画布上,颜色缓冲区
storeOp: 'store',
loadOp: 'clear',
clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0},//渲染的背景颜色
}]
})
//给渲染通道设置渲染管线,可设置多条渲染管线
renderPass.setPipeline(pipeline);
//设置顶点数据缓冲区,即上述定义标记为0的缓冲区,也可以为其它
renderPass.setVertexBuffer(0, vertexBuffer);
renderPass.setBindGroup(0, bindGroup);
//绘制命令
renderPass.draw(6);
//绘制完结束绘制
renderPass.end()
//将生成的GPU命令存入GPU命令缓冲区
const commandBuffer = commandEncoder.finish()
device.queue.submit([commandBuffer])
window.requestAnimationFrame(render)
}
render();
</script>
</body>
</html>