介绍
最近感觉工作不好找,想着另辟蹊径学点不一样的东西。做一个小小的转行,然后就有了从0开始学习WebGPU这个事情了
为什么要学习WebGPU
- WebGPU更好地支持多线程
- WebGPU支持compute shader,从而让程序员能利用GPU实现很多优化
- WebGPU与WebGL2的区别很大,两者不容易兼容。如果要从WebGL1升级,最好直接升级到WebGPU,一劳永逸
- WebGPU是标准,各大浏览器都会支持。不像WebGL2,苹果直接不支持。
- 目前WebGPU应该已经算正式发布,也比较成熟了,也有相关的Demo可供学习
- 个人对WebGPU将来还是挺看好的,海外的几个大厂都有人在研究。类似苹果电脑这块,将webGPU作为跨平台的载体使用的话,Mac电脑的游戏也能变多并且也已经有hlsl转wgsl的工具,移植起来感觉很方便
一、 初始化WebGPU
1. 判断浏览器是否支持WebGPU
2. 获取GPU的JS逻辑实例
const adapter = await navigator.gpu.requestAdapter({
powerPreference: "high-performance" // 非填,设置WebGPU在高性能下运行
});
const device = await adapter.requestDevice();
3. 配置显示GPU渲染结果的画布
<canvas id="webgpu"></canvas>
#webgpu {
width: 500px;
height: 500px;
}
const canvas = document.getElementById("webgpu");
const context = canvas.getContext("webgpu");
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format
});
二、 配置GPU管线
1. 创建顶点着色器
WebGPU使用wgsl着色器语言,作为着色器外部资源。详情请看着色器语言WGSL快速了解
@vertex
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 3>(
vec2<f32>(0.0, 0.5),
vec2<f32>(-0.5, -0.5),
vec2<f32>(0.5, -0.5)
);
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}
@fragment
fn main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
// 顶点向量
import vertex from "../src/assets/triangle.vert.wgsl?raw";
const vertexShader = device.createShaderModule({
code: vertex
});
// 颜色向量
import frag from "../src/assets/red.frag.wgsl?raw";
const fragmentShader = device.createShaderModule({
code: frag
});
2. 创建GPU管线
await device.createRenderPipelineAsync({
layout: "auto", // 必填,不填报错
vertex: {
module: vertexShader,
entryPoint: "main" // wgsl文件的入口函数名
},
fragment: {
module: fragmentShader,
entryPoint: "main",
targets: [{
format // 指定颜色类型
}]
},
primitive: {
topology: "triangle-list"
}
});
三、 录制 Commadnd 队列
1. 录制命令
const encoder = device.createCommandEncoder();
const renderPass = encoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
loadOp: "clear",
clearValue: { r: 0.5, g: 0.5, b: 0.5, a: 1 },
storeOp: "store"
}]
});
2. 设置管线
renderPass.setPipeline(pipeline);
3. 绘制顶点
renderPass.draw(3);
renderPass.end();
4. 提交队列
const buffer = encoder.finish();
device.queue.submit([buffer]);