Skip to content

指南 · 专家

性能优化(draw call / InstancedMesh)、内存释放、z-fighting 与视锥剔除、WebGPURenderer 与 TSL、与 Babylon.js 对比。版本基线 three r184(2026-04)。

一、性能:draw call 与合批

每次渲染一个 Mesh 大致对应一次 draw call(CPU 向 GPU 提交绘制)。上千次 draw call 时,瓶颈往往在 CPU 提交而非 GPU 算力。降 draw call 是 Three.js 性能优化的第一抓手。

海量同物体用 InstancedMesh:一份几何 + 一份材质,一次绘制大量副本。

js
const count = 10000;
const mesh = new THREE.InstancedMesh(geometry, material, count);
const m = new THREE.Matrix4();
for (let i = 0; i < count; i++) {
  m.setPosition(Math.random() * 100, 0, Math.random() * 100);
  mesh.setMatrixAt(i, m);
}
mesh.instanceMatrix.needsUpdate = true;
scene.add(mesh); // 一万个方块 → 1 次 draw call

其他手段:合并静态几何(BufferGeometryUtils.mergeGeometries)、共用材质、纹理图集(多图合一减少纹理切换)、LOD(按距离换低模)。

renderer.info.render.calls 看每帧 draw call、renderer.info.render.triangles 看三角形数。

二、内存释放:dispose

WebGL 没有自动垃圾回收——scene.remove(mesh) 只断开场景图引用,几何/材质/纹理的 GPU 资源不会被回收,必须显式释放:

js
function disposeMesh(mesh) {
  mesh.geometry.dispose();
  const mats = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
  for (const mat of mats) {
    for (const v of Object.values(mat)) {
      if (v && v.isTexture) v.dispose(); // 材质上的所有贴图
    }
    mat.dispose();
  }
}
scene.remove(mesh);
disposeMesh(mesh);

renderer.info.memory.geometries/textures 监控持有量;数字只增不减就是泄漏。频繁加载/卸载场景(如 SPA 路由切换)尤其要注意。

三、z-fighting 与视锥剔除

z-fighting(深度闪烁):深度缓冲精度有限且在 near~far 间非线性分布(近处精、远处疏)。把 near 设极小 + far 极大会严重稀释精度,远处共面物体争夺深度而闪烁。

  • 对策:near 尽量大、far 尽量小(够用即可);必要时建渲染器时开 logarithmicDepthBuffer: true(移动端支持有限、略慢)。

视锥剔除(frustum culling):Three.js 用每个对象的 boundingSphere/boundingBox 判断是否在视锥内、跳过不可见对象。

手改顶点后要重算包围体

手动改写 BufferGeometry 顶点后,旧包围体未更新,可能把视野内的物体误剔除/闪烁。改完调用 geometry.computeBoundingSphere()(和 computeBoundingBox());临时可对该 mesh 设 frustumCulled = false

四、WebGPURenderer 与 TSL(2026 新一代)

Three.js 正在向 WebGPU 演进。WebGPURendererthree/webgpu 引入,需异步初始化,底层走 WebGPU、不支持时可回退 WebGL

js
import * as THREE from "three/webgpu";
import { color, texture, uv, positionLocal } from "three/tsl";

const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init(); // 异步初始化(或直接用 setAnimationLoop)

// 节点材质 + TSL 自定义着色
const material = new THREE.MeshStandardNodeMaterial();
material.colorNode = texture(myTexture).mul(color(0xff8800));

**TSL(Three.js Shading Language)**用可组合的 JS 节点描述着色逻辑,由 Three.js 编译期生成后端代码。优势:

  • 同一份 TSL 可编译到 WebGPU(WGSL) 与 WebGL(GLSL) 两种后端
  • 类型安全、可复用、可组合,免去 GLSL 字符串拼接与 onBeforeCompile hack。

现状(r184):WebGLRenderer 仍是默认、最成熟、教程最多的路径;WebGPU/TSL 在持续成熟,适合需要 compute shader、节点材质、前沿效果的场景。新项目可评估,生产主力仍多在 WebGL。

五、与 Babylon.js 对比

维度Three.jsBabylon.js
定位轻量灵活的通用 3D 库开箱即用的全功能引擎
内建能力渲染/场景图为主,其余靠 addons/第三方物理、碰撞、Inspector、节点材质编辑器、GUI、动画
心智像积木,自由组合像游戏引擎,约定更多
生态最大(react-three-fiber、drei…)完整但相对小,官方工具链强
底层WebGL(默认)/ WebGPUWebGL / WebGPU
选型要灵活、轻量、大生态、Web 集成要开箱全功能、游戏向、内建工具

二者都支持 glTF、都基于 WebGL/WebGPU。简单可视化/创意网页/与前端框架深度集成倾向 Three.js;完整游戏/需要内建编辑器与物理倾向 Babylon.js。

六、专家级易错点

  • removedispose:显存泄漏——几何/材质/纹理都要释放。
  • 颜色贴图忘设 colorSpace:画面偏暗发灰——map/emissiveMapSRGBColorSpace,数据贴图保持默认。
  • near 极小 + far 极大:z-fighting——收紧深度范围。
  • 海量独立 Mesh:draw call 爆炸——改 InstancedMesh/合批。
  • 手改顶点不重算包围体:误剔除——computeBoundingSphere()
  • 改相机参数忘 updateProjectionMatrix():画面变形。
  • 以为 WebGPURenderer 无需初始化:它需 await renderer.init()

回到 参考 查核心类、材质/光照/加载器与版本现状速查表。