为什么要用WebGPU
webgpu是面向现代的web处理gpu的图形API,由于之前的webgl的2.0对应的opengl的是2010版本,gpu显卡的迅猛发展让厂商开始慢慢抛弃openGL的老旧存在缺陷的api,为了适应新的时代发展,才有了最新的webgpu的发展,底层抛弃了opengl,D3D12(D3D12 [Direct3D 12]是Microsoft开发的图形API)、Metal(Apple)、Vulkan(Khronos Group开发的跨平台的图形API)所实现。在编译阶段处理大量的cpu的损耗。
device.queue.submit([commandEncoder.finish()]);
同时在webgpu可以通过多线程处理,在web worker中可以单独引入device的队列,submit你的指令集。
WebGPU 和 WebGL 区别
上下文区分
,首先webGPU的上下文是区分多个,而不是webgl只有一个上下文。每块单独负责一个区域的工作, 所以你再也不用像之前返回的webgl的gl上下文,从这个gl中处理所有工作。
性能好
,与现有用于浏览器图形加速的WebGL相比,WebGPU将底层接口从老旧的OpenGL升级到了最新的Direct3D 12、Vulkan和Metal,所以这也使得它既拥有了比过去高得多的执行效率。
更高效的api
,webgl开发绘制多个图形,你要做循环大量的初始gl定义,而webgpu可以保存之前buffer设置等
......
GPUDevice创建资源,例如纹理和缓冲区。
GPUCommandEncoder允许对单个命令进行编码,包括渲染和计算过程。
完成后,它变成GPUCommandBuffer对象,可以提交给GPUQueueGPU 执行。
可以将渲染结果呈现给 HTML 画布
之前我们获取webgl的上下文使用canvas.getContext('webgl')
,那么同样在webGPU如何获取上下文呢?
const context = canvas.getContext('webgpu') as GPUCanvasContext;
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = canvas.getContext('webgpu') as GPUCanvasContext;
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.clientWidth * devicePixelRatio;
canvas.height = canvas.clientHeight * devicePixelRatio;
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
alphaMode: 'premultiplied',
在context.configure为alphaMode
指定字符串 ‘opaque’。alphaMode
设置的是 Canvas 和 HTML 元素背景的混合方式。如果设置为’opaque’,则用 WebGPU 绘图内容完全覆盖。也可以为alphaMode
设置为 ‘premultiplied’ (相当于alpha预乘),在这种情况下,作为 WebGPU 绘图的结果,如果画布像素的 alpha 小于 1,则该像素将是画布和 HTML 元素背景混合的颜色。
所以你要开发WebGPU,你的浏览器必须使用edge或chrome,并升级到113版本以上的开发者版本
vscode提供wgsl文件更好的高亮支持的插件
绘制一个三角形
import { makeSample, SampleInit } from '../../components/SampleLayout';
import triangleVertWGSL from '../../shaders/triangle.vert.wgsl';
import redFragWGSL from '../../shaders/red.frag.wgsl';
const init: SampleInit = async ({ canvas, pageState }) => {
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
if (!pageState.active) return;
const context = canvas.getContext('webgpu') as GPUCanvasContext;
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.clientWidth * devicePixelRatio;
canvas.height = canvas.clientHeight * devicePixelRatio;
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
alphaMode: 'premultiplied',
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: device.createShaderModule({
code: triangleVertWGSL,
entryPoint: 'main',
fragment: {
module: device.createShaderModule({
code: redFragWGSL,
entryPoint: 'main',
targets: [
format: presentationFormat,
primitive: {
topology: 'triangle-list',
function frame() {
if (!pageState.active) return;
const commandEncoder = device.createCommandEncoder();
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
view: textureView,
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
loadOp: 'clear',
storeOp: 'store',
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.draw(3, 1, 0, 0);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
requestAnimationFrame(frame);
requestAnimationFrame(frame);
* 渲染入口,将init、以及其他web页面配置传入渲染页面
const HelloTriangle: () => JSX.Element = () => {
* nextjs在运行时编译配置存在的全局变量
* __filename 文件路径
* __dirname 目录路径
* __filename.substring(__dirname.length + 1) 文件名
return makeSample({
name: 'Hello Triangle',
description: 'Shows rendering a basic triangle.',
init,
sources: [
name: __filename.substring(__dirname.length + 1),
contents: __SOURCE__,
name: '../../shaders/triangle.vert.wgsl',
contents: triangleVertWGSL,
editable: true,
name: '../../shaders/red.frag.wgsl',
contents: redFragWGSL,
editable: true,
filename: __filename,
export default HelloTriangle;
这里用的是webgpu-samples的源码,是一个不错入门的开源项目,该项目用nextjs构建并同构渲染,服务端渲染页面、客户端监听路由传参请求服务端更新渲染页面。
简单的执行流程:
将device挂载到webgpu上下文,初始化gpu
开始写入我的着色器的执行方式,将着色器源码等传入生成渲染管线
生成程序对象,传入渲染管线并定义buffer绘制方式
requestAnimationFrame绘制帧
CommandEncoder 编码器
const commandEncoder = device.createCommandEncoder();
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
view: textureView,
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
loadOp: 'clear',
storeOp: 'store',
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.draw(3, 1, 0, 0);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
其实就是将缓冲区的指令打包给gpu,类似一个webgl的程序对象。所以为什么更高效,因为gl配置都可以缓存。
这里注解对应是顶点着色器和图元着色器, @builtin注入对应的全局变量,这里之前定义了draw绘制3个点,所以这里会返回3个点, -> 符号代表返回对应的全局位置信息。vec4 代表4维向量,每个向量存储浮点数32位。
在@fragment这里@location(0)对应fragment.targets
数组中的第0个格式。
在webgpu在未来替代webgl的可能性是很大的,相比之前的webgl更方便了我们的开发,并提供了极大的灵活性,对于复杂图形渲染带来了更好的性能提升。在Babylon已经完全支持WebGPU的支持,同时Threejs也逐步在支持。卷一卷还是有必要的。
为什么我的WebGL开发这么丝滑 🌊
女朋友想学webGL修图,安排!
卷不动?学学简单的webGL矩阵算法
立体感十足的数据可视化:我的WebGL 3D环状图制作分享
本文正在参加「金石计划」