Skip to content

是否支持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,
});

创建顶点缓冲区

JavaScript 类型化数组

webgpu坐标系(以画布中心为原点): 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整型
f1616位浮点数
f3232位浮点数
vec2二维向量
mat2x2二维矩阵

矩阵描述为列向量,如下矩阵表示为:var m = mat3x3<f32>(a,d,g, b,e,h, c,f,i);

[abcdefghi]\begin{bmatrix} a &b &c \\ d &e &f \\ g &h &i \end{bmatrix}

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()]);

变化矩阵

平移

[100Tx010Ty001Tz0001]\begin{bmatrix} 1 &0 &0 &T_{x} \\ 0 &1 &0 &T_{y} \\ 0 &0 &1 &T_{z} \\ 0 &0 &0 &1 \end{bmatrix}

缩放

[Sx0000Sy0000Sz00001]\begin{bmatrix} S_{x} &0 &0 &0 \\ 0 &S_{y} &0 &0 \\ 0 &0 &S_{z} &0 \\ 0 &0 &0 &1 \end{bmatrix}

旋转

绕Z轴旋转γ\gamma

[cosγsinγ00sinγcosγ0000100001]\begin{bmatrix} \cos\gamma &-\sin\gamma &0 &0 \\ \sin\gamma &\cos\gamma &0 &0 \\ 0 &0 &1 &0 \\ 0 &0 &0 &1 \end{bmatrix}

绕X轴旋转α\alpha

[10000cosαsinα00sinαcosα00001]\begin{bmatrix} 1 &0 &0 &0 \\ 0 &cos\alpha &-\sin\alpha &0 \\ 0 &sin\alpha &cos\alpha &0 \\ 0 &0 &0 &1 \end{bmatrix}

绕Y轴旋转β\beta

[cosβ0sinβ00100sinβ0cosβ00001]\begin{bmatrix} cos\beta &0 &sin\beta &0 \\ 0 &1 &0 &0 \\ -sin\beta &0 &cos\beta &0 \\ 0 &0 &0 &1 \end{bmatrix}

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;
}