跳到主要内容
3D Playground
GPU 与 WebGPU 计算第 9 / 11 节

three.js WebGPURenderer 与 TSL

进阶WebGPU
此示例需要 WebGPU(Chrome / Edge 113+、Safari 26+)。

WebGPURenderer

import * as THREE from "three/webgpu" 提供新的 WebGPURenderer。它是异步初始化的,要先 await renderer.init()。如果浏览器不支持 WebGPU(或拿不到 adapter),它会自动退回 WebGL 2 后端,场景代码一行都不用改。状态栏会显示当前实际使用的后端;把 FORCE_WEBGL 设为 true 可以主动体验后备路径。

TSL:用函数调用写着色器

TSL(Three Shading Language)不是字符串,而是一棵用 JS 函数搭出来的节点图。three.js 再把它编译成目标语言:WebGPU 上是 WGSL,WebGL 2 上是 GLSL。

js
const noise = mx_noise_float(positionLocal.mul(uFreq).add(vec3(0, t, t.mul(0.5))));
blobMat.positionNode = positionLocal.add(normalLocal.mul(noise.mul(uAmp)));
blobMat.colorNode = palette(noise.mul(0.35).add(uHue));

运算写成方法链:.mul()、.add();Fn() 定义可复用的着色器函数。

节点材质

MeshPhysicalNodeMaterial 是完整的 PBR 材质,你只替换其中几个“插槽”:

  • positionNode:顶点阶段,这里沿法线做噪声位移;
  • colorNode:基础色,这里用余弦调色板;
  • emissiveNode:自发光,这里用菲涅尔项做发光的边缘。

光照、透明涂层、色调映射照常由 three.js 负责。

Uniform 节点

uniform(0.22) 返回一个节点,它的 .value 在 JS 里随时可改,每帧自动上传。GUI 滑块直接改这些值,不需要重新编译着色器。

计算节点

js
const positions = instancedArray(init, "vec3");
const orbit = Fn(() => {
  const p = positions.element(instanceIndex);
  p.assign(/* rotated p */);
})().compute(COUNT);
renderer.compute(orbit);

instancedArray 是一个存储缓冲,.compute(COUNT) 生成每个粒子一次调用的计算着色器。同一个缓冲用 toAttribute() 交给精灵材质作为每实例位置,数据不经过 CPU。

如果检测到软件 adapter(如 SwiftShader),示例会跳过每帧的计算 pass:软件模拟的计算太慢。粒子仍会显示,只是不再旋转。

核心 API

THREE.WebGPURendererrenderer.initMeshPhysicalNodeMaterialFnuniformmx_noise_floatpositionNodeinstancedArrayrenderer.computeSpriteNodeMaterial

动手试试

  1. 1把 FORCE_WEBGL 改成 true,确认状态栏显示 WebGL 2 后端,画面几乎一样。
  2. 2把 colorNode 换成 vec3(noise.mul(0.5).add(0.5)),直接看噪声的灰度值。
  3. 3把 emissiveNode 里的 pow 指数从 3.0 改成 1.0,边缘光会怎样变化?
运行中