唯杰地图 logo唯杰地图
VJMAP
EN

WebGPU 渲染引擎

vjmap3d/references/advance/webgpu.md 17.7 KB235 行
原文

版本要求

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 注入。自定义材质有三种选择:

  1. TSL 节点材质(推荐):使用 three.js 的 three/tsl NodeMaterial 体系编写,可同时运行于 WebGPU 与 WebGL2 后端;
  2. CustomShaderMaterial 的 tslNodes 节点注入:与 GLSL 注入并存,按引擎自动选择(见下);
  3. 过渡方案:直接使用默认的 webgl 引擎。forceWebGL: true 仍是 WebGPURenderer(仅换 WebGL2 后端),同样只认 NodeMaterial,GLSL ShaderMaterial/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 的效果(含自定义 GLSL Effect)会尝试自动转译为 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 呈镜像(两端各自自洽)。

相关示例