<!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>Threejs中文网:http://www.webgl3d.cn/</title>
</head>
<body>
<!-- canvas:用来展示WebGPU渲染的结果 -->
<canvas id="webgpu" width="500" height="500"></canvas>
<script type="module">
// 顶点着色器、片元着色器代码
import { vertex, fragment } from './shader.js'
// 配置WebGPU上下文
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const canvas = document.getElementById('webgpu');
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device: device,
format: format,
});
//创建顶点数据
// 一个矩形拆分为两个三角形表示,三角形其中两个顶点坐标是重合的
// 注意一个面的多个三角形,正反面要保持一致,要么都是正面,要么都是反面,或者说沿着某个方向看过去,要么都是顺时装,要么都是逆时针
const vertexArray = new Float32Array([
// 三角形1三个顶点坐标的x、y、z值
-0.3, -0.5, 0.0,//顶点1坐标
0.3, -0.5, 0.0,//顶点2坐标
0.3, 0.5, 0.0,//顶点3坐标
// 三角形2三个顶点坐标的x、y、z值
-0.3, -0.5, 0.0,//顶点4坐标 与顶点1重合
0.3, 0.5, 0.0,//顶点5坐标 与顶点3重合
-0.3, 0.5, 0.0,//顶点6坐标
]);
// 定点缓冲区
const vertexBuffer = device.createBuffer({
size: vertexArray.byteLength, //顶点数据的字节长度
//usage设置该缓冲区的用途(作为顶点缓冲区|可以写入顶点数据)
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
//引入gl-matrix.js库
import * as glMatrix from './dist/esm/index.js';
// 将顶点信息写入缓冲器
device.queue.writeBuffer(vertexBuffer, 0, vertexArray);
// ----------------------------------------------------------------
// 创建一个单位矩阵
const mat4Array = glMatrix.mat4.create();
// 在GPU显存上创建一个uniform数据缓冲区
const mat4Buffer = device.createBuffer({
size: mat4Array.byteLength,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
// mat4Array里面矩阵数据写入uniform缓冲区mat4Buffer
device.queue.writeBuffer(mat4Buffer, 0, mat4Array);
//------------------
// ----------------------------------------------------------------
// 给片元着色器传递一个颜色数据
const colorArray = new Float32Array([1.0, 1.0, 0.0]); //绿色
// 在GPU显存上创建一个uniform数据缓冲区
const colorBuffer = device.createBuffer({
size: colorArray.byteLength,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
// colorArray里面颜色数据写入uniform缓冲区colorBuffer
device.queue.writeBuffer(colorBuffer, 0, colorArray);
// 创建渲染管线
const pipeline = device.createRenderPipeline({
layout: "auto",
vertex: {
//顶点相关配置
buffers: [
// 顶点所有的缓冲区模块设置
{
//其中一个顶点缓冲区设置
arrayStride: 3 * 4, //一个顶点数据占用的字节长度,该缓冲区一个顶点包含xyz三个分量,每个数字是4字节浮点数,3*4字节长度
attributes: [
{
// 顶点缓冲区属性
shaderLocation: 0, //GPU显存上顶点缓冲区标记存储位置
format: "float32x3", //格式:loat32x3表示一个顶点数据包含3个32位浮点数
offset: 0, //arrayStride表示每组顶点数据间隔字节数,offset表示读取改组的偏差字节数,没特殊需要一般设置0
},
],
},
],
module: device.createShaderModule({
label: "triangle vertex",
code: vertex,
}),
entryPoint: "main",
},
fragment: {
module: device.createShaderModule({
label: "fragment vertex",
code: fragment,
}),
entryPoint: "main", //指定入口函数
targets: [
{
format: format, //和WebGPU上下文配置的颜色格式保持一致
},
],
},
primitive: {
topology: "triangle-list", //绘制三角形
// topology: "point-list", //绘制三角形
// topology: "line-list", //绘制三角形
},
});
// 设置uniform数据的绑定组
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0), //绑定组,标记为0
// 一个组里面可以包含多个uniform数据
entries: [
//每个元素可以用来设置一个uniform数据
{
binding: 0, //标记组里面的uniform数据
resource: { buffer: mat4Buffer },
},
{
//binding的值对应@binding(1)的参数,保持一致,比如都是1
binding: 1, //标记组里面的uniform数据
resource: { buffer: colorBuffer },
},
],
});
//渲染循环
let angle = 0.0; //初始旋转角度
function render() {
angle += 0.01; //每次渲染角度增加
const modelMatrix = glMatrix.mat4.create();
glMatrix.mat4.translate(modelMatrix, modelMatrix,[0,0,0.5]);
// 每次渲染,生成新的旋转矩阵
glMatrix.mat4.rotateY(modelMatrix, modelMatrix, angle);
//模型矩阵modelMatrix重新写入uniform数据的缓冲区中
device.queue.writeBuffer(mat4Buffer, 0, modelMatrix);
// 创建GPU命令编码器对象
const commandEncoder = device.createCommandEncoder();
const renderPass = commandEncoder.beginRenderPass({
label: "our basic canvas renderPass",
// 给渲染通道指定颜色缓冲区,配置指定的缓冲区
colorAttachments: [
{
// 指向用于Canvas画布的纹理视图对象(Canvas对应的颜色缓冲区)
// 该渲染通道renderPass输出的像素数据会存储到Canvas画布对应的颜色缓冲区(纹理视图对象)
view: context.getCurrentTexture().createView(),
storeOp: "store", //像素数据写入颜色缓冲区
loadOp: "clear",
clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1.0 }, //背景颜色
},
],
});
// 关联顶点缓冲区数据和渲染管线
renderPass.setVertexBuffer(0, vertexBuffer);
// 把绑定组里面的uniform数据传递给着色器中uniform变量
// 参数1的0和.getBindGroupLayout(0)参数一致,都是0
renderPass.setBindGroup(0, bindGroup);
renderPass.setPipeline(pipeline);
renderPass.draw(6); // call our vertex shader 3 times
renderPass.end();
// 命令缓冲器
const commandBuffer = commandEncoder.finish();
device.queue.submit([commandBuffer]);
requestAnimationFrame(render);
}
render();
</script>
</body>
</html>
---------------
// 顶点着色器代码
const vertex = /* wgsl */ `
//@group(0)的参数0对应webgpu代码.getBindGroupLayout(0)参数0
//@binding(0)的参数对应webgpu代码.binding的值,保持一致,比如都是0
@group(0) @binding(0) var<uniform> S:mat4x4<f32>;
struct Out{
@builtin(position) position:vec4<f32> ,
@location(0) vposition:vec3<f32>
}
@vertex
fn main(@location(0) pos: vec3<f32>) -> Out{
var out:Out;
out.position= S * vec4<f32>(pos,1.0);//缩放矩阵对顶点缩放变换
out.vposition= (S * vec4<f32>(pos,1.0)).xyz;//缩放矩阵对顶点缩放变换
return out;
}
`;
// 片元着色器代码
const fragment = /* wgsl */ `
// uniform关键字辅助var声明一个三维向量变量color表示片元颜色
//@binding(1)的参数对应webgpu代码.binding的值,保持一致,比如都是1
@group(0) @binding(1) var<uniform> color:vec3<f32>;
@fragment
fn main(@location(0) vposition:vec3<f32>) -> @location(0) vec4<f32> {
var c=color;
return vec4<f32>(vposition.z+c.x,c.y,c.z+1-vposition.z,1);
}
`;
export { vertex, fragment };