共计 5064 个字符,预计需要花费 13 分钟才能阅读完成。
如何创立前端 WebGPU 我的项目?
1. 丐版 HelloWebGPU
最简略的 WebGPU 程序应该是这样的:
<script>
const init = () => {if ('gpu' in navigator) {console.log(navigator.gpu)
} else {console.log('浏览器不反对 WebGPU;浏览器未开启 WebGPU 性能。')
}
}
init()
</script>
将下面的代码保留成 index.html
,双击关上就能够在控制台看到输入的对象:
如果你是什么都不会的老手,那么接下来这几个问题我只说一次。当然,新手能够选择性跳过。
① 为什么是 index.html
不为什么,你喜爱能够改成 home.html
,main.html
,然而文件的后缀名得是 html
,用 index.html
是习惯。
② 你这个代码为什么浏览器没有显示货色?
如果你没有浏览过我之前的文章,没有仔细阅读过,天然是不晓得为什么。
WebGPU
不像 WebGL
,它解除了与 DOM – HTMLCanvasElement 的强关联。很简略:
WebGPU focus on GPU. Rendering is part of GPU’s features, not all.
意思是,渲染绘图只是 GPU 的一部分性能,WebGPU 更靠近古代 GPU 的功能定位。
所以,作为浏览器内置的一个 API,你只须要 JavaScript 就能够获取到这套全新设计的 API,它挂载于 navigator
上。
③ 为什么你不必 HTTP 协定关上 index.html
别急,待会就用。
双击 “index.html” 文件,浏览器关上这个页面,你能够在浏览器地址栏看到残缺的文件资源门路:
file:///C:/Users/<YourUserName>/Desktop/index.html
最后面的字母是 file:///
,而后跟了个本地磁盘门路,这种形式叫做 应用 file 协定拜访(关上)页面 ,然而咱们很多时候都是应用 http(或 https,简略起见,下文全用 http)协定关上的,哪怕是在本地:
http://localhost:8080/index.html
具体起因不反复,网上有十分多这两种协定在 Web 开发时的差别。
而在本地启动一个 HTTP 服务器软件,伺服某个磁盘中的资源,在我是学生的年代材料不算多,也不算少,能够借助 Apache-HTTPD,也能够借助 Tomcat,甚至为了一个小小的 index.html 下载一个 XAMPP 的大有人在。
当初,你齐全能够借助:
- VSCode 的 LiveServer 插件
- 更易配置的 nginx / openresty
- Python 的 http.server 模块
- NodeJS 全局 http 插件 / NodeJS 写一个繁难的 HTTP 服务器
- 借助各种前端工程化工具,应用 devServer
来伺服这个 index.html,我之后将抉择最初一种,作为开发时的辅助工具。
2. 我须要类型提醒
2202 年如果还有人倡议老手用 Notepad 写代码的都倡议去喝一口恒河水。软件开发至今,积攒了有数的技术和门槛,就不要再重叠了。
我抉择 VSCode 作为 JavaScript 代码编辑器,你也能够抉择别的。
写 JavaScript 这种动静类型的语言,调用函数、鼠标指针挪动到变量、函数上没有智能提醒时,纯猜、靠背是相当让人没有急躁的事件。
咱们能够借助 TypeScript 的 类型申明 来做到类型提醒。
很惋惜,在我写这篇文章的时候,正式频道的各大浏览器还没有正式裸露 WebGPU JavaScript API。
2.1. 应用 VSCode 的 jsconfig 获取类型提醒
VSCode 是 TypeScript 编写的,天然对类型申明文件(*.d.ts
)有反对。在一般的 JavaScript 我的项目中,想要取得像 TypeScript 一样的类型提醒,只需在我的项目根目录下减少一个 jsconfig.json
文件,其领有 compilerOptions.typeRoots
配置项,通知 VSCode 要去哪里读取类型申明文件即可。
如上所示,jsconfig.json
通知 VSCode,类型申明文件从 ./src
文件夹下找。在那个 index.d.ts
文件中,你能够本人写类型申明;我就偷懒了,从官网仓库复制了一份 WebGPU 的类型申明文件,粘贴于此。
所以,在 main.js 文件就能够用 WebGPU 的类型提醒啦!
这种只是一种过渡办法,接下来将展现在 Vite + TypeScript 我的项目中应用 WebGPU 官网的类型提醒库。
2.2. 在前端工程化的我的项目中应用类型提醒
我的抉择是 Vite + TypeScript,抉择 Vite 的理由很简略:
- 开发服务器应用 esbuild 速度足够快
- 插件蓬勃发展,开箱反对很多 Webpack 须要 loader 能力实现的个性
- 文档丰盛
- 内置依赖少
最重要的一点,我比拟腻烦 polyfill 的过程。毕竟,
能运行 WebGPU 的浏览器,还须要思考向下兼容?
对于如何应用 Vite 脚手架工具开启各种模板我的项目就不复述了,我应用 Vanilla + TS 模板创立了一个工程“gpuweb-prj”:
随后,装置我的项目依赖:
pnpm install && pnpm add @webgpu/types -D
而后,在我的项目根目录下的 tsconfig.json
的 compilerOptions.typeRoots
增加类型提醒库的门路即可:
{
"compilerOptions": {"typeRoots": [ "./node_modules/@webgpu/types", "./node_modules/@types"]
}
}
“./node_modules/@types” 是一个类型提醒库仓库的默认门路,位于 GitHub – DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.,然而 WebGPU 没有提交到这个仓库,所以须要增加新的门路,与 2.1 大节中增加本人的门路是一个情理,只不过这里用的是 TypeScript 我的项目,所以是 tsconfig.json 文件。
而后,你就能在你的 ts 代码中欢快地用 WebGPU 类型了。
待 WebGPU 正式登陆各大浏览器的那一天,应该就不须要搞这么麻烦了,届时会像 WebGL 一样,将类型集成在 TypeScript 的内置类型申明中:
:) 应该快来了吧,心愿官网别鸽太久了。
2.3. 在 Vue/React 等我的项目中
也一样的,均是在 jsconfig.json / tsconfig.json 文件中配置类型申明文件的门路即可。
3. 画三角形的例子
基于上述应用 Vite 创立的我的项目,批改 main.ts 如下:
import {wgslShader} from './shader'
import './style.css'
const init = async () => {if (!('gpu' in navigator)) {return}
const canvas = document.getElementById('canvas') as HTMLCanvasElement
//#region 适配高 DPR 屏幕的 canvas
const dpr = window.devicePixelRatio
canvas.style.height = `${canvas.height / dpr}px`
canvas.style.width = `${canvas.width / dpr}px`
//#endregion
const adapter = await navigator.gpu.requestAdapter()
if (!adapter) {return}
const device = await adapter.requestDevice()
const ctx = (canvas as HTMLCanvasElement).getContext('webgpu')
if (!ctx) {return}
const preferFormat = navigator.gpu.getPreferredCanvasFormat()
ctx.configure({
device: device,
format: preferFormat,
alphaMode: 'opaque',
})
const triangleShader = device.createShaderModule({code: wgslShader})
const pipeline = device.createRenderPipeline({
layout: "auto",
vertex: {
module: triangleShader,
entryPoint: 'vertex_main'
},
fragment: {
module: triangleShader,
entryPoint: 'frag_main',
targets: [{format: preferFormat}]
},
primitive: {topology: 'triangle-list'}
})
const renderPassDescriptor = {
colorAttachments: [{
view: undefined,
clearValue: [0, 0, 0, 1],
loadOp: 'clear',
storeOp: 'store',
}],
} as GPURenderPassDescriptor
const frame = () => {if (!canvas) {return}
const commandEncoder = device.createCommandEncoder();
(renderPassDescriptor.colorAttachments as GPURenderPassColorAttachment[])[0].view = ctx.getCurrentTexture().createView()
const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor)
renderPassEncoder.setPipeline(pipeline)
renderPassEncoder.draw(3)
renderPassEncoder.end()
device.queue.submit([commandEncoder.finish()
])
requestAnimationFrame(frame)
}
requestAnimationFrame(frame)
}
window.addEventListener('DOMContentLoaded', () => {init()
})
创立着色器文件 shader.ts 如下:
export const wgslShader = /* wgsl */`
@vertex
fn vertex_main(@builtin(vertex_index) vertex_index: 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[vertex_index], 0.0, 1.0);
}
@fragment
fn frag_main() -> @location(0) vec4<f32> {return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
`
款式和 HTML 如下:
<div id="app">
<canvas id="canvas" height="800" width="1800"></canvas>
</div>
<style>
html, body {
margin: 0;
padding: 0;
}
#app {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
</style>
其余改变并不大。
4. 作者的话
前端工程化蓬勃发展离不开 NodeJS,尽管这玩意儿本意并不是前端,只是它为一系列前端构建、开发工具提供了一个不错的土壤。
前端系统化离不开 TypeScript,它应该算是呈现在了适合的工夫。
前端走向各种丰盛的利用,ng、react、vue 堪称是浓墨重彩的一笔了,然而调用 GPU 的 Web 图形开发者更要搞清楚的是: 你是在操作各种 DOM,还是操心图形 API?