AI 3D建模工具: Tripo 3D | Meshy AI

一句话结论:three.js 场景一旦上到几千个动态对象,帧预算常常花在 JavaScript 的对象更新、矩阵运算和把 draw call 交给 GPU 上。2026 年 10 月出现的开源引擎 WAMR3D 换了个分工:把场景图、世界矩阵、视锥剔除、绘制排序、射线检测放进用 Rust 编译的 WebAssembly 内核,再配一套从零写的 WebGPU 渲染器,外层保留一套读起来像 three.js 的 API。作者公布的基准里,10,000 个独立动画网格 @1920×1080:WAMR3D 两种 API 都跑满 60 fps、CPU 提交约 12.2–12.3 ms;three.js WebGPU 9.5 fps / 100.5 ms,three.js WebGL 33.7 fps / 23.7 ms。同一份基准也写了反面:细粒度小调用比 three.js 慢 1.3–3.8 倍。

披露:本文编译自 WAMR3D 作者 Aymen 的发布文(dev.to,2026-10-14)。作者是引擎维护者;基准来自其仓库记录(单机、单次、中位数),文中的所有性能数字都应按你自己的场景复测。


一、它是什么:不是 three.js 的包装

WAMR3D 是一个 WebAssembly 3D 渲染引擎:Rust 内核 + 独立的 WebGPU 渲染器。它不是 three.js 的封装,也不是成熟的替代品——项目很年轻,README 自己就这么写。

三个组成部分:

部分 内容
WASM 原生内核 场景图、世界矩阵、视锥剔除、绘制排序、射线检测都在 Wasm 模块里
自研 WebGPU 渲染器 网格、线、点、精灵、实例化网格;Basic / Lambert / Standard 材质;颜色贴图、雾、最多 16 盏灯、PCF 阴影、色调映射、渲染目标、4× MSAA
两套 API wamr3d/compat 下是 three.js 风格对象 API;另有面向大场景的低层批量 API

许可 MIT,无运行时依赖,Wasm 模块约 488 KiB。

二、为什么把内核放进 Wasm

在大多数 three.js 应用里,场景状态存在于 JavaScript 对象上。每一次 position.set()、rotation.y +=、矩阵更新都是 JS 的工作,垃圾回收最终会介入。

WAMR3D 的做法不同:

  • 场景存在引擎内存里。 对象在 WebAssembly 里显式创建和销毁,引擎本身不引入 GC 暂停。
  • 分代句柄(generational handles)。 指向已销毁对象的句柄,不会悄悄指向另一个对象。
  • 零拷贝几何。 通过 typed array 视图直接读写引擎内存中的顶点属性。
  • 批量同步。 对象 API 里 position/rotation/scale 仍留在 JS,但每帧把改动一次性发送给引擎。

结果是一个明确的取舍:批量工作时 WAMR3D 赢,大量细碎调用时它输。项目把两面都写进了文档。

三、基准:先看清条件

数字来自仓库的基准记录,2026-10-14,headless Chrome、Windows x64、RTX 2070 SUPER,每种分辨率单次运行的中位数。作者明确说:这只是起点,请在自己的场景里测。

10,000 个独立动画网格 @1920×1080

引擎 fps Draw 提交(CPU)
WAMR3D(批量 API) 60.0 12.2 ms
WAMR3D(对象 API) 60.0 12.3 ms
three.js WebGPU 9.5 100.5 ms
three.js WebGL 33.7 23.7 ms

50,000 个实例化物体,场景在 CPU 上更新

引擎 场景更新(CPU)
WAMR3D 批量 API 12.0 ms
three.js WebGPU 39.1 ms
three.js WebGL 39.7 ms

换成作者的话:在这个 showcase 里,WAMR3D 的 draw 提交 CPU 时间大约是 three.js WebGPU 的 1/8、three.js WebGL 的 1/1.9。帧率封顶 60 是 showcase 的显示限制。

反面:小调用更慢

项目也公布了另一面:做很少工作的调用(例如单次向量运算、一次改一个属性),比 three.js 慢 1.3 到 3.8 倍,因为进入 WebAssembly 有固定开销。GPU 时间没有单独测,只体现在帧率里。

读法:这不是"全面更快",而是"批量提交快、单点调用慢"。你的场景越像"几万个对象、每帧整体更新",越适合;越像"每帧对几十个对象做几百次细粒度 getter/setter",越不适合。

四、正确性:7,600 个对照用例

速度只有在输出正确时才有意义。WAMR3D 用 three.js r186 源码生成的 7,600 多个参照用例回归它的 Rust 实现:

  • 单个操作的结果一致到约百万分之一;
  • 23 个渲染器测试用例与保存的 three.js 图像逐像素比对;
  • 643 个 Rust 测试覆盖单元、行为一致性(parity)与内存分配;
  • 仓库还有一个视觉一致性页面,把 WAMR3D 渲染结果、three.js 图像和像素差异并排显示。

这套做法的价值在于:"像 three.js"不是口头承诺,而是可回归的对照。它也划出了边界——对照的是 three.js r186 的语义,不是所有 three.js 生态插件。

五、两分钟上手

npm install wamr3d

改 import、初始化一次、切到 WebGPU 渲染器:

- import * as THREE from 'three';
+ import * as THREE from 'wamr3d/compat';
+ await THREE.init();

- const renderer = new THREE.WebGLRenderer({ antialias: true });
+ const renderer = new THREE.WebGPURenderer({ antialias: true });
+ await renderer.init();

一个完整例子:

import * as WAMR3D from 'wamr3d/compat';

await WAMR3D.init();

const scene = new WAMR3D.Scene();
const camera = new WAMR3D.PerspectiveCamera(50, innerWidth / innerHeight, 0.1, 100);
camera.position.set(3, 2, 5);
camera.lookAt(0, 0, 0);

const cube = new WAMR3D.Mesh(
  new WAMR3D.BoxGeometry(),
  new WAMR3D.MeshLambertMaterial({ color: 0x4f8cff })
);
scene.add(cube, new WAMR3D.DirectionalLight(0xffffff, 2));

const renderer = new WAMR3D.WebGPURenderer({ antialias: true });
await renderer.init();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

renderer.setAnimationLoop((time) => {
  cube.rotation.y = time / 1000;
  renderer.render(scene, camera);
});

对 WAMR3D 支持的类,scene.add、position.set、rotation.y +=、lookAt、射线检测都按原样工作。迁移指南列出了具体有哪些类、要注意什么。

要求:支持 WebGPU 的浏览器(当前 Chrome 或 Edge),通过 HTTP 而不是 file:// 提供页面;Node.js 18+ 可以在没有渲染器的情况下跑数学与场景图。

六、大场景用批量 API

对象超过几百个之后,收益主要来自批量 API:句柄与 setRotations 之类的调用一次跨进 WebAssembly,更新成千上万个对象;世界矩阵通过 typed array 视图读取,不复制。

低层 API 直接从 wamr3d 导出:

import { init, World } from 'wamr3d';

await init();

const world = new World();
const scene = world.createObject();
const arm = world.createObject();
const hand = world.createObject();
world.add(scene, arm);
world.add(arm, hand);

world.setPosition(arm, 10, 0, 0);
world.setRotation(arm, 0, 0, Math.PI / 2, 'XYZ');
world.setPosition(hand, 1, 0, 0);

world.updateMatrixWorld(scene, false);
console.log(world.matrixWorld(hand)); // Float32Array(16), column-major

七、什么时候选它,什么时候别选

你的场景 建议 理由
数万实例化物体、批量动画 值得试 批量 API 的收益最大
数字孪生/城市/点云式大场景 值得试 剔除、矩阵、排序在 Wasm 内核
需要大量 three.js 生态插件 暂缓 compat 覆盖的是核心类,不是整个生态
每帧大量细粒度属性读写 暂缓 跨 Wasm 调用有固定成本,慢 1.3–3.8×
必须支持 Safari/旧设备 暂缓 依赖 WebGPU;且项目年轻
需要成熟的光照/后处理全家桶 暂缓 材质与后处理覆盖有限,需自行核对

八、一个可复现的评估流程

  1. 先跑官方 showcase(wamr3d.org/examples/showcase),四种渲染器同页对比,确认在你的机器上确实有差距。
  2. 换成你自己的模型:GLB 走一遍加载、材质、阴影、实例化。可以先用 glTF 查看器 确认模型本身没有材质/贴图问题,再进引擎。
  3. 记录三个数:draw 提交 CPU 时间、场景更新 CPU 时间、帧率;同时记分辨率、对象数、是否实例化。
  4. 测最差路径:每帧对单个对象做几十次属性读写,看是否掉到 three.js 以下。
  5. 验收视觉:把同一相机角度的渲染结果与 three.js 对比;必要时用 3D 查看器 打开源模型作为参照。
  6. 确认部署条件:WebGPU 可用性、页面必须走 HTTP(S)、Wasm 488 KiB 的加载预算。

九、结论

WAMR3D 的价值不在于"打败 three.js",而在于把 Web 3D 的性能瓶颈换个位置:把每帧的 JavaScript 场景管理搬进 Wasm,把绘制交给自研 WebGPU 渲染器,用 60 fps vs 9.5 fps 这类数字证明路线可行;同时用 小调用慢 1.3–3.8 倍 提醒你它不是万能。

如果你的项目是"大场景 + 批量更新 + 愿意接受年轻引擎",它值得一个下午的实测;如果你要的是"成熟生态 + 细粒度控制 + 全平台兼容",three.js 仍然是更稳的默认选项。


Sources

同主题阅读

本文属于「Web3D / 游戏引擎」专题。BimAnt 编译整理,转载请注明出处。基准为单机单次中位数,实际性能请以你的场景复测为准。