介绍
- WebGL 在 2011 年左右首次出现后,在图形功能方面彻底改变了网络。WebGL 是 OpenGL ES 2.0 图形库的 JavaScript 端口,允许网页将渲染计算直接传递给设备的 GPU 进行处理以非常高的速度,并在元素内渲染结果 <canvas>。
- WebGPU 是 WebGL 的继任者(与 OpenGL ES 无关),提供了与现代 GPU 更好的兼容性、支持通用 GPU 计算、更快的操作以及访问更高级的 GPU 功能。
- WebGPU 的开发始于 2017 年,这项技术的目标是借助现代 GPU 的计算能力来加速图形和计算并且允许在 Web 上进行高性能 3D 图形和数据并行计算。
- 与 DirectX 和 Vulkan 等其他图形 API 类似,WebGPU 也提供了直接在 GPU 上渲染和访问高级功能的功能,从而提高图形渲染的速度和效率。
- WebGPU 大幅减少相同图形的 JavaScript 工作量,以及为机器学习模型推理提供 3 倍以上的改进。因为 WebGPU 提供了更灵活的 GPU 编程和访问,从而实现 WebGL 无法提供的高级功能。
- 目前已在 Chrome 113 Beta 中默认启用。
GPU on the Web Community Group
- GPU 规范由 Web Community Group 发布,但它不是 W3C 标准,也不在 W3C 标准轨道上。WebGPU 是 W3C 的 GPU for the Web 社区组共同努力的结果,其中包括 Mozilla、Apple、Intel 和 Microsoft 等大公司的贡献。经过 6 年的开发(90 位贡献者,2000 次提交,3000 次发布),从 2017 年的初始设计开始,现在可以在 Chrome 中使用的第一个实现,并在支持 Firefox 和 Safari 进行中。
- GPU on the Web Community Group 的使命是提供 Web 平台与本机系统平台中存在的现代 3D 图形和计算功能之间的接口。
- 目标是设计一个新的 Web API,以高效、强大和安全的方式公开这些现代技术,与现有平台 API 一起工作,例如 Microsoft 的 Direct3D 12、Apple 的 Metal 和 The Khronos Group 的 Vulkan。即由浏览器封装了现代图形 API(Dx12、Vulkan、Metal),提供给 Web 3D 程序员,将为 Web 释放更多的 GPU 硬件的功能。
- WebGPU 的这个初始版本是未来更新和增强的构建基础。API 将提供更高级的图形功能,鼓励开发人员发送对其他功能的请求。Chrome 团队还计划提供对着色器核心的更深入访问,以便在 WGSL(WebGPU 着色语言)中进行更多的机器学习优化和额外的人体工程学。
- Chromium 的 Dawn 库和 Firefox 的 wgpu 库都可以作为独立包使用,它们提供了出色的可移植性和人体工程学层,可以抽象操作系统 GPU API。在本机应用程序中使用这些库还可以更轻松地通过 Emscripten和 Rust web-sys移植到 WASM。
- 一些 WebGL 库已经在实施对 WebGPU 的支持,这意味着使用 WebGPU 可能只需要进行一行更改:
- Babylon.js已经拥有完整的 WebGPU 支持。
- PlayCanvas宣布了最初的 WebGPU 支持。
- TensorFlow.js支持大多数运算符的 WebGPU 优化版本。
- Three.js WebGPU 支持正在进行中,请参阅示例。
发展
- 2018 年起,Google Chrome 团队就已经宣布着手 WebGPU 标准的实现工作。
- 2022 年 11 月,商用开源 3D 引擎 Cocos 发布了支持 WebGPU 的新版本 Cocos Creator 3.6.2,为国内首个支持该渲染后端的开源引擎。
- 时至今日,WebGPU 的各类接口、生态、应用已日趋完善,WebGPU 1.0 已于 2023 年初正式推出。
- 作为 Google、Apple、Mozilla 等浏览器厂商共同推进的次时代图形标准,WebGPU 毫无疑问会在未来取代 WebGL,这也是 Cocos 投资 WebGPU 技术的核心原因。
- 从时间上来看,WebGPU 的出现时间稍晚,但也正因如此,让 WebGPU 得以借助次时代图形 API 的经验,做出更好的设计。未来随着 WebGPU 标准在主流浏览器的逐步落地,其能力将给 H5、小程序等的内容创作与性能表现带来更多可能,也一定会在 Web 平台出现不逊于原生 app 的图形渲染效果,同时基于 Web 端的优势给用户带来更轻量和便捷的体验。
使用
- 首先,确保用户的浏览器支持 WebGPU。可以使用以下代码检查浏览器是否支持 WebGPU:1 
 2
 3
 4
 5if ('gpu' in navigator) { 
 // WebGPU is supported
 } else {
 // WebGPU is not supported
 }
- 要使用 WebGPU,首先需要获取一个 GPUDevice对象。这是与物理 GPU 交互的接口。以下代码用于获取 GPU 设备:1 
 2
 3
 4
 5async function getGPUDevice() { 
 const adapter = await navigator.gpu.requestAdapter();
 const device = await adapter.requestDevice();
 return device;
 }
- 为画布(canvas)创建一个 WebGPU 上下文:1 
 2const canvas = document.querySelector('canvas'); 
 const context = canvas.getContext('webgpu');
- 使用 GPU 设备和上下文创建一个 GPUSwapChain:1 
 2
 3
 4
 5
 6
 7
 8
 9
 10function createSwapChain(device, context) { 
 const swapChainFormat = 'bgra8unorm';
 const swapChainDescriptor = {
 device: device,
 format: swapChainFormat
 };
 context.configureSwapChain(swapChainDescriptor);
 }
- 在 WebGPU 中,渲染管道定义了如何将顶点、纹理等数据转换为像素。创建一个管道需要提供顶点着色器、片段着色器和其他一些描述符:1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34async function createRenderPipeline(device) { 
 const vertexShaderModule = device.createShaderModule({
 code: `...` // GLSL or WGSL code for the vertex shader
 });
 const fragmentShaderModule = device.createShaderModule({
 code: `...` // GLSL or WGSL code for the fragment shader
 });
 const pipelineDescriptor = {
 vertex: {
 module: vertexShaderModule,
 entryPoint: 'main',
 buffers: [/* ... */], // Vertex buffer layout
 },
 fragment: {
 module: fragmentShaderModule,
 entryPoint: 'main',
 targets: [
 {
 format: 'bgra8unorm',
 },
 ],
 },
 primitive: {
 topology: 'triangle-list',
 },
 depthStencil: {/* ... */}, // Depth and stencil state (if needed)
 multisample: {/* ... */}, // Multisampling configuration (if needed)
 };
 const pipeline = device.createRenderPipeline(pipelineDescriptor);
 return pipeline;
 }
- 创建缓冲区、纹理等资源,并编写绘图命令。这需要使用 GPUBuffer、GPUTexture等对象,并使用GPUCommandEncoder记录命令。
- 最后,创建一个渲染循环,用于在每一帧中绘制场景。在渲染循环中,首先需要获取一个 GPUTexture,然后使用管道和资源进行绘图:1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25function renderLoop() { 
 const texture = context.getCurrentTexture();
 const renderPassDescriptor = {
 colorAttachments: [
 {
 view: texture.createView(),
 loadValue: [0, 0, 0, 1],
 storeOp: 'store',
 },
 ],
 };
 const commandEncoder = device.createCommandEncoder();
 const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
 passEncoder.setPipeline(pipeline);
 // Set bind groups, vertex buffers, index buffers, etc.
 passEncoder.draw(/* ... */);
 passEncoder.endPass();
 const commandBuffer = commandEncoder.finish();
 device.queue.submit([commandBuffer]);
 requestAnimationFrame(renderLoop);
 }
示例
参考资料
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 张坤的博客!
 评论