WebGPU 渲染引擎
版本要求
webgpu 引擎、app.whenReady()、exportRenderTargetAsync / renderTargetToDataUrlAsync 均为 v2.0.0 起的 API——旧版 vjmap3d@1.0.23(webgl-only,peer three@^0.165)的 dist 中没有这些符号。npm 当前最新版 vjmap3d@2.0.0(peer three@>=0.185)已包含全部本节能力;若你装的是 1.x 请升级或改用 webgl 引擎。
vjmap3d v2 起支持 WebGPU 渲染引擎:创建应用时通过渲染参数 render.engine 即可在 webgl(默认,WebGLRenderer)与 webgpu(WebGPURenderer,基于 three/webgpu 与 TSL 节点材质体系)之间选择。业务代码(实体、模块、事件、内置材质、后期处理、粒子等)在两种引擎下保持同一套写法,SDK 内部会按引擎自动选择对应实现。
为什么用 WebGPU
WebGPU 是新一代 Web 图形标准,相比 WebGL 具有更低的驱动开销、更好的多线程与计算能力(Compute Shader)。在大场景、大量实例、复杂后期处理的情况下有更好的性能上限;同时 three.js 的 TSL(Three Shading Language)节点材质可同时编译到 WGSL(WebGPU)与 GLSL(WebGL2 后端),一份材质两端可用。
浏览器要求
| 环境 | 支持情况 |
|---|---|
| Chrome / Edge 113+ | 默认开启(需 HTTPS 或 localhost 环境) |
| Firefox / Safari 较新版本 | 逐步支持中,部分版本需在设置中开启实验特性 |
| 不支持 WebGPU 的环境 | three 的 WebGPURenderer 自动降级为 WebGL2 后端(仍属 webgpu 引擎:app.renderer.isWebGPURenderer===true、仍走 TSL 材质,GLSL ShaderMaterial 依旧不可用) |
可通过 navigator.gpu 判断浏览器是否支持 WebGPU:
const supportWebGPU = typeof navigator !== "undefined" && !!navigator.gpu;
启用 WebGPU 引擎
创建 App 时在 render(渲染引擎模块参数)中设置 engine: "webgpu":
// 创建一个服务类,与服务连接
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
container: "map", // 容器id
render: {
engine: "webgpu", // 渲染引擎: 'webgl'(默认) 或 'webgpu'
antialias: true,
clearColor: 0x10141c
},
camera: {
viewHelper: { enable: true }
}
})
// webgpu 引擎的渲染器需要异步初始化:访问 app.renderer 前先等待就绪
await app.whenReady();
app.logInfo("当前渲染引擎: " + (app.renderer && app.renderer.isWebGPURenderer
? (app.renderer.backend?.isWebGPUBackend ? "WebGPU" : "WebGL2 后端(WebGPURenderer)")
: "WebGL(webgl 引擎)"), "success", 5000);
//
// 之后的业务代码与 webgl 引擎完全一致
app.addDefaultLights();
let box = new THREE.Mesh(
new THREE.BoxGeometry(2, 2, 2),
new THREE.MeshStandardMaterial({ color: 0x4488ff, roughness: 0.35, metalness: 0.15 })
);
box.position.set(0, 1, 0);
vjmap3d.Entity.fromObject3d(box).addTo(app);
app.signal.onAppUpdate.add(() => { box.rotation.y += 0.01; });
// 等价通道:app.on("onAppUpdate", cb) 或 Module.onAppUpdate —— 三者同源于 callModules 扇出(见 core/app.md)
渲染参数
在 render 中与 WebGPU 相关的参数:
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| engine | "webgl" | "webgpu" |
"webgl" |
渲染引擎。webgpu 时使用 three/webgpu 的 WebGPURenderer |
| forceWebGL | boolean |
false |
webgpu 引擎时强制使用 WebGL2 后端(WebGPURenderer + WebGLBackend):保留 TSL 新材质体系,同时兼容不支持 WebGPU 的环境 |
其余 render 参数(antialias / clearColor / pixelRatio / autoClear 等)两种引擎通用。另外 webgpu 引擎的 powerPreference 只接受 "high-performance" 与 "low-power"("default" 会被忽略)。
异步就绪 whenReady
webgl 引擎的渲染器是同步创建的;webgpu 引擎的渲染器需要异步初始化(内部 await renderer.init(),并动态加载 three/webgpu、three/tsl 与 TSL 材质实现,webgl 用户零开销)。
因此创建 App 后如需立即访问 app.renderer,应先 await app.whenReady():
const app = new vjmap3d.App(svc, { container: "map", render: { engine: "webgpu" } });
await app.whenReady(); // webgl 引擎下立即 resolve,两种引擎通用
console.log(app.renderer.isWebGPURenderer); // true
渲染器就绪前,SDK 会自动跳过每帧的渲染阶段(更新阶段照常执行),不会产生报错。
建议在 whenReady 之后再创建场景内容
内置材质的自动切换发生在材质创建时。webgpu 的 TSL 材质实现是异步按需加载的,若在渲染器就绪之前创建这些材质,会回退到 GLSL 实现并在控制台告警(webgpu 下无法正确渲染)。因此 webgpu 引擎下建议把创建实体/材质的业务代码统一放在 await app.whenReady() 之后。
自动回退
浏览器不支持 WebGPU 本身不会回退到 webgl 引擎:three 的 WebGPURenderer 会在内部换用 WebGL2 后端(控制台告警 WebGPU is not available, running under WebGL2 backend),此时仍是 webgpu 引擎(WebGPURenderer + TSL,GLSL ShaderMaterial 不可用)。
以下情况才会回退到 webgl 引擎(WebGLRenderer),并在控制台输出提示:
three/webgpu/three/tsl动态加载失败(如 importmap 缺映射)或渲染器init()抛错;- 做为唯杰地图三维图层使用时(
map2d模式,共享 2D 地图的 WebGL 上下文):该模式不支持webgpu引擎。
判断当前实际使用的引擎:
await app.whenReady();
if (app.renderer.isWebGPURenderer) {
// WebGPURenderer:可能是 WebGPU 后端,也可能是 WebGL2 后端(forceWebGL,或浏览器不支持 WebGPU 时 three 自动降级)
console.log(app.renderer.backend?.isWebGPUBackend ? "WebGPU 后端" : "WebGL2 后端");
} else {
console.log("WebGLRenderer(已回退 webgl 引擎)");
}
内置能力的自动切换
webgpu 引擎下,SDK 内部所有基于 GLSL 的内置实现都会自动切换为对应的 TSL / WebGPU 实现,用法与 webgl 引擎完全一致:
| 类别 | 内容 |
|---|---|
| 特效材质 | FlowMaterial 流动纹理(含 createTextFlowMaterial 文本滚动 / createRenderElementFlowMaterial)、RadarMaterial 雷达扫描、createFlowGradientMaterial 建筑流光渐变、AnimatorFrameMaterial 序列帧、SpriteAnimator 精灵动画、HolographicMaterial 全息投影、HighlightMaterial 高亮、FixedSizeMeshMaterial 固定像素大小、GridMaterial 网格 |
| 反射 / 透射 | createReflectorShaderMesh(ReflectorShaderMaterial / ReflectorDudvMaterial)、MeshReflectorMaterial、MeshTransmissionMaterial |
| 线 / 文本 / 标注 | PolylinesEntity 线(Line2,含逐段线宽/透明度/虚线)、MeshLine、Text 文本(SDF,含描边)、Label 标签、SymbolEntity 符号点、Outlines 描边对象 |
| 内置对象 | Heatmap 热力图(二维/三维)、createFlyline 飞线、TrailLine 轨迹线、ScreenQuad 屏幕四边形(⚠️ 例外:自定义 GLSL fragmentShader/vertexShader/uniforms 选项在 webgpu 下被丢弃、仅显示 map 纹理;自定义着色请改传 material)、体积渲染(MeshEntity.setHeatmapDataMaterial 的体纹理盒渲染路径) |
| 地图 / 地形 | MapView / MapViewEntity 底图瓦片与地形高程(HEIGHT_SHADER) |
| 粒子系统 | ParticleRenderModule 全部粒子模式(billboard / stretched / mesh / trail 拖尾 / 软粒子) |
| Shadertoy | ShadertoyMaterial / ShadertoyTexture(GLSL 自动转译为 TSL,见下文) |
| 后期处理 | 辉光(bloom)、选中/描边(selected/outline)、FXAA / SMAA / AO / 景深 / 色调映射 / 色深 / 像素化 / 故障 / 模糊,以及自定义效果(见下文) |
| 其他 | 天空盒 / 环境贴图(PMREMGenerator、getEnvmapFromScene)、截图与图像导出、深度拾取(unprojectAsync)等 |
双引擎统一的 uniforms 用法
webgpu 下的 TSL 内置材质同样暴露 uniforms 字典与同名属性存取器,uniforms.xxx.value 的运行期改值写法两种引擎相同。⚠️ 但 webgl 下只有 shaderMaterial() 工厂生成的材质(FlowMaterial 及 createTextFlowMaterial/createRenderElementFlowMaterial、AnimatorFrameMaterial、RadarMaterial)才有同名属性存取器;HolographicMaterial、FixedSizeMeshMaterial、createFlowGradientMaterial、ShadertoyMaterial、反射材质在 webgl 下写同名属性会被静默忽略——跨引擎代码请统一用 uniforms.xxx.value:
let mat = new vjmap3d.RadarMaterial(
{ uRadius: 3, uColor: new THREE.Color("#00ffff"), uSpeed: 2 },
{ uTime: app.commonUniforms.iTime }
);
// 以下写法两种引擎通用:
mat.uniforms.uColor.value = new THREE.Color("#ff0000"); // uniforms 字典
mat.uSpeed = 3; // 同名属性存取器(仅工厂材质,见上)
mat.opacity = 0.8; // three 内建属性(⚠️ webgl 下雷达着色器不读 opacity,无效果)
Shadertoy:GLSL 自动转译
webgpu 引擎下,ShadertoyMaterial 与 ShadertoyTexture 会在运行时把 shadertoy 的 GLSL(mainImage)自动转译为 TSL 节点,再由 three 编译到 WGSL 运行,无需修改 shader 代码:
- 支持
iTime/iResolution/iMouse(实时鼠标交互)/iChannel0-3输入通道 / 自定义uniforms,以及#define宏与#ifdef条件编译; - webgl 版的通用选项(
opacity/brightOpacity/hsbOffset/uvRotateAngle/uvScale/repeat/ 背景色与背景纹理混合 /sizeAttenuation等)同样生效; - 少数复杂 GLSL 语法可能转译失败:此时自动回退为基础材质并在控制台告警(按 shader 去重),不会报错中断;
- 已知差异:
iTime在webgpu下使用渲染器时钟,不随动画timeScale缩放与暂停。
自定义材质
webgpu 引擎(含 forceWebGL 及自动降级的 WebGL2 后端)不支持直接使用 GLSL 编写的 THREE.ShaderMaterial / RawShaderMaterial / onBeforeCompile 注入。自定义材质有三种选择:
- TSL 节点材质(推荐):使用 three.js 的
three/tslNodeMaterial体系编写,可同时运行于 WebGPU 与 WebGL2 后端; CustomShaderMaterial的tslNodes节点注入:与 GLSL 注入并存,按引擎自动选择(见下);- 过渡方案:直接使用默认的
webgl引擎。forceWebGL: true仍是WebGPURenderer(仅换 WebGL2 后端),同样只认 NodeMaterial,GLSLShaderMaterial/RawShaderMaterial/onBeforeCompile依旧不可用(three 报Material "ShaderMaterial" is not compatible并以默认节点材质渲染)。
CustomShaderMaterial 的 TSL 节点注入
webgl 引擎下 CustomShaderMaterial 通过 GLSL 字符串(vertexShader / fragmentShader)注入;webgpu 引擎下改用 tslNodes 提供 TSL 节点(colorNode / positionNode / opacityNode 等)。两者可同时提供,SDK 按当前引擎自动选择,做到一份代码双引擎兼容:
const mat = new vjmap3d.CustomShaderMaterial({
baseMaterial: THREE.MeshStandardMaterial,
color: 0x4488ff,
uniforms: { uProgress: { value: 0 } },
// webgl 引擎: GLSL 注入
fragmentShader: /* glsl */`
uniform float uProgress;
void main() {
csm_DiffuseColor.rgb = mix(csm_DiffuseColor.rgb, vec3(1.0, 0.0, 0.0), uProgress);
}
`,
// webgpu 引擎: TSL 节点注入(tsl 为 three/tsl 导出集合, uniforms 为对应的 TSL uniform 节点)
tslNodes: (tsl, uniforms) => ({
colorNode: tsl.mix(tsl.materialColor.rgb, tsl.vec3(1, 0, 0), uniforms.uProgress)
})
});
// 运行期改 uniform,两种引擎写法一致
mat.uniforms.uProgress.value = 0.5;
tslNodes 可以是对象,也可以是函数 (tsl, uniformNodes) => nodes 延迟构建。webgpu 下仅提供 GLSL 而没有 tslNodes 时,会在控制台告警并返回未注入自定义效果的基础材质(不会崩溃)。
后期处理
webgpu 引擎下,后期处理由 WebGPU 后处理模块(基于 three 的 RenderPipeline + TSL 节点)自动接管,对外 API 与 webgl 引擎保持一致:
entity.selected/entity.bloom/entity.outline用法不变;postProcess模块参数仅部分生效(enable/renderUpdate/useFXAA/useSMAA/autoAdd{Selected,Outline,Bloom}Pass/bloomPass/outlinePass/selectedPass,其余如disableMergeEffect/renderTargetOptions/depthPickingPass被忽略;bloomPass.radius语义不同——是 mip 权重,默认 0);app.composer是兼容外壳(passes恒为空数组);app.bloomEffect只有selection/intensity/luminanceThreshold,app.outlineEffect/app.selectedEffect只有selection/edgeStrength/visibleEdgeColor/hiddenEdgeColor/pulseSpeed;app.getDepth改为场景射线求交实现(不依赖postProcess.enable);addEffectRenderPass/removeEffectRenderPass/setEffectRenderPassEnable/setEffectRenderPassOrder继续可用:内置 pmndrs 效果中只有KawaseBlurPass、PixelationEffect、ColorDepthEffect、ToneMappingEffect(固定 Reinhard)、DepthOfFieldEffect(固定参数)、SSAOEffect(GTAO)、TextureEffect映射到 TSL,FXAAEffect/SMAAEffect在此路径上为空操作(抗锯齿请用postProcess.useFXAA/useSMAA);其余含mainImage的效果(含自定义 GLSLEffect)会尝试自动转译为 TSL,只有mainUv的效果(如GlitchEffect)直接告警跳过——故障效果请用便捷方法addGlitchEffect()(混入在app上);也可直接传 TSL 效果函数(原生写法);addSSAOEffect/addDepthOfFieldEffect/addKawaseBlurPass/addToneMappingEffect/addColorDepthEffect/addPixelationEffect/addGlitchEffect等便捷方法均有webgpu等价实现(可传参数,如addPixelationEffect(granularity)、addKawaseBlurPass({ sigma });两种引擎都不返回句柄)。WebGPU 后处理模块在渲染器就绪后才挂载,须在await app.whenReady()之后调用——之前调用会落到 webgl 版 mixin(无 composer)被静默丢弃。
详见 后期处理 文档中的 WebGPU 部分。
截图与图像导出
app.getScreenshot({})截图两种引擎通用(参数对象不可省略:getScreenshot()无参时内部读params.download抛 TypeError、Promise reject);webgpu下 GPU 读回是异步的:同步的exportRenderTarget在webgpu引擎下无法工作(会告警并返回空),请改用异步版本exportRenderTargetAsync/renderTargetToDataUrlAsync(webgl引擎下这些异步版本同样可用,内部自动分流)。
// 两种引擎通用的渲染目标导出
const blob = await vjmap3d.exportRenderTargetAsync(app.renderer, renderTarget, "image/png");
const dataUrl = await vjmap3d.renderTargetToDataUrlAsync(app.renderer, renderTarget);
注意事项与已知限制
- 裁剪平面:材质级
clippingPlanes在webgpu后端不生效,这是 three r185 的行为约定,需改用 three 的ClippingGroup把受裁剪的对象包起来(仅webgl引擎的WebGLRenderer支持材质级clippingPlanes;forceWebGL仍是WebGPURenderer,同样需ClippingGroup); - 动态环境反射:基于
CubeCamera的动态环境(如DynamicEnvironment)在webgpu引擎下暂不支持,可改用getEnvmapFromScene对场景做一次性 PMREM 环境贴图烘焙(功能对等,但失去逐帧动态反射); - 加载动画:内置 loading 动画的 shadertoy 特效在
webgpu下可能转译失败并自动回退,如遇到可在 loading 选项中设置showShadertoy: false, showHtml: true改用 HTML 样式; - 单页面引擎混用:同一页面创建多个
App时建议使用同一渲染引擎;两个不同引擎的App并发存活期间交错创建材质可能导致分流错误(控制台有明确告警),先销毁再创建的顺序用法不受影响; - TypeScript 类型:
@types/three版本与所装 three 主版本对齐(npm 版 vjmap3d@2.0.0 对应three@>=0.185/@types/three@^0.185);工程tsconfig的moduleResolution建议bundler或node16(three/webgpu、three/tsl为子路径导出),否则需开启skipLibCheck。
已知视觉差异
webgpu 与 webgl 引擎渲染结果整体一致,个别效果存在轻微、可接受的观感差异:
- 选中/描边效果的边缘略粗、略亮(three
OutlineNode与 pmndrs 算法差异,SDK 已做强度对齐补偿); - 辉光(bloom)强度换算语义略有不同,相同参数下观感可能略偏弱/偏强,可微调
intensity; - 雷达/飞线/轨迹线/粒子等特效的中间调在
webgpu下更亮:webgpu统一走 sRGB 输出转换(色彩上更正确),webgl 旧实现是直写线性值; - 透射材质(
MeshTransmissionMaterial)的折射偏移垂直方向与 webgl 呈镜像(两端各自自洽)。