是否支持WebGPU
获取GPU设备对象
javascript
const adapter = await navigator.gpu?.requestAdapter();
const device = await adapter?.requestDevice();
配置WebGPU上下文
javascript
let canvas = document.querySelector('#webgpu'); // canvas画布
const context = canvas.getContext('webgpu');
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device: device,
format: presentationFormat,
});
创建顶点缓冲区
webgpu坐标系(以画布中心为原点):
javascript
// 顶点数据
const vertexArray = new Float32Array([
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0,
]);
// 顶点缓冲区
const vertexBuffer = device.createBuffer({
size: vertexArray.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
// 将顶点数据写入顶点缓冲区
device.queue.writeBuffer(vertexBuffer, 0, vertexArray);
创建渲染管线
javascript
// 渲染管线
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
// 顶点缓冲区
buffers: [{
arrayStride: 3 * 4, //一个顶点数据占用的字节长度
attributes: [{
shaderLocation: 0, //GPU显存上顶点缓冲区标记存储位置
format: "float32x3", //格式:loat32x3表示一个顶点数据包含3个32位浮点数
offset: 0 //顶点缓冲区中顶点数据在显存中的偏移量
}]
}],
// 顶点着色器
module: device.createShaderModule({
code: vertex, //顶点着色器代码
}),
entryPoint: "main", //顶点着色器入口函数
},
fragment: {
// 片元着色器
module: device.createShaderModule({
code: fragment, //片元着色器代码
}),
entryPoint: "main", //片元着色器入口函数
targets: [{
format: presentationFormat, //渲染通道的像素格式
}]
},
primitive: {
topology: "triangle-list" //顶点绘制方式
}
});
顶点绘制方式 | 描述 |
---|---|
point-list | 绘制点 |
line-strip | 绘制线 |
triangle-list | 绘制三角形 |
顶点着色器与片元着色器
WGSL基础类型
类型 | 描述 |
---|---|
bool | 布尔值 |
u32 | 无符号整型 |
i32 | 整型 |
f16 | 16位浮点数 |
f32 | 32位浮点数 |
vec2 | 二维向量 |
mat2x2 | 二维矩阵 |
矩阵描述为列向量,如下矩阵表示为:var m = mat3x3<f32>(a,d,g, b,e,h, c,f,i);
javascript
const vertex = `
@vertex
fn main(@location(0) pos: vec3<f32>) -> @builtin(position) vec4<f32> {
return vec4<f32>(pos,1.0); //pos转齐次坐标
}
`
const fragment = `
@fragment
fn main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);//片元设置为红色
}
`
export { vertex, fragment }
创建渲染命令
javascript
// 创建GPU命令编码器对象
const commandEncoder = device.createCommandEncoder();
const renderPass = commandEncoder.beginRenderPass({
// 给渲染通道指定颜色缓冲区,配置指定的缓冲区
colorAttachments: [{
view: context.getCurrentTexture().createView(),
storeOp: 'store', //像素数据写入颜色缓冲区
loadOp: 'clear',
clearValue: { r: 1, g: 1, b: 1, a: 1.0 }, //背景颜色
}]
});
// 设置渲染管线
renderPass.setPipeline(pipeline);
// 设置顶点缓冲区
renderPass.setVertexBuffer(0, vertexBuffer);
// 绘制命令
renderPass.draw(3);
// 结束绘制
renderPass.end();
// 完成并提交命令
device.queue.submit([commandEncoder.finish()]);
变化矩阵
平移
缩放
旋转
绕Z轴旋转
绕X轴旋转
绕Y轴旋转
WebGPU传递uniform数据
javascript
// 矩阵数据
const mat4Array = new Float32Array([
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
])
// 在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 bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0), // 设置组,对应@group(0)
entries: [
{
binding: 0, // 绑定位置,对应@binding(0)
resource: {
buffer: mat4Buffer
}
} // 可继续添加其他uniform数据
]
});
// 设置uniform数据
renderPass.setBindGroup(0, bindGroup);
WGSL
@group(0) @binding(0) var<uniform> S:mat4x4<f32>;
@vertex
fn main(@location(0) pos: vec3<f32>) -> @builtin(position) vec4<f32> {
return S * vec4<f32>(pos,1.0);
}
片元着色器的坐标
以左上角为原点,向右为x轴,向下为y轴,数值为画布大小。
WGSL
@fragment
fn main(@builtin(position) fragCoord : vec4<f32>) -> @location(0) vec4<f32> {
var x = fragCoord.x; //片元屏幕坐标x
var y = fragCoord.y; //片元屏幕坐标y
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
顶点数据的插值计算
WGSL
// 顶点着色器
struct Out{
@builtin(position) position:vec4<f32>,
@location(0) color:vec4<f32>
}
struct Input{
@location(0) pos:vec3<f32>,
@location(1) color:vec3<f32>
}
@vertex
fn main(input:Input) -> Out {
var out:Out;
out.position = vec4<f32>(input.pos,1.0);
out.color = vec4<f32>(input.color, 1.0);
return out;
}
WGSL
// 片元着色器
struct Input{
@location(0) color:vec4<f32>
}
@fragment
fn main(input:Input) -> @location(0) vec4<f32> {
return input.color;
}