Three.js 性能优化实战——让3D场景流畅运行

摘要

本文系统梳理了 Three.js 性能优化的核心策略,强调优化是一项涵盖资源、渲染与代码的系统性工程。在资源加载层面,文章建议通过建模工具减少多边形数量以实现模型轻量化,采用压缩格式与纹理图集(Atlas)降低显存与切换开销,并利用 LoadingManager 实现资源的异步与按需加载。在渲染层面,重点介绍了利用 InstancedMesh 实现批量渲染以大幅减少绘制调用(Draw Call),通过 LOD(层级细节)技术根据相机距离动态切换模型精度,以及合理运用视锥体裁剪剔除视野外物体。在代码层面,文章建议使用 BufferGeometry、合并相似材质网格,并严格避免在动画循环中创建新对象以减轻垃圾回收压力,从而全面提升 3D 场景的运行帧率与流畅度。

目录

一、为什么要优化性能

二、资源加载优化

[1. 模型轻量化](#1. 模型轻量化)

[2. 纹理优化](#2. 纹理优化)

[3. 异步加载](#3. 异步加载)

三、渲染优化

[1. 使用 InstancedMesh 批量渲染](#1. 使用 InstancedMesh 批量渲染)

[2. LOD 层级细节](#2. LOD 层级细节)

[3. 视锥体裁剪](#3. 视锥体裁剪)

四、代码优化建议


一、为什么要优化性能

在复杂 3D 场景中,性能问题往往成为制约体验的关键因素。Three.js 性能优化是一个系统性工程,需要从资源、渲染、代码和硬件四个层面综合施策。

二、资源加载优化

1. 模型轻量化

模型文件过大是性能瓶颈的常见原因。建议使用 Blender 等工具减少多边形数量------将高精度模型从 10 万面降低至 2 万面,帧率可提升 30% 以上。

2. 纹理优化

  • 纹理压缩:采用 ASTC、ETC2 格式压缩纹理,减少显存占用

  • 纹理合并:将多个小纹理合并为一张大图(Atlas),可降低 90% 的纹理切换开销

3. 异步加载

使用 LoadingManager 监控加载进度,优先加载首屏可见资源:

javascript

复制代码
const manager = new THREE.LoadingManager();
manager.onProgress = (url, loaded, total) => {
    console.log(`加载中:${loaded}/${total}`);
};
const loader = new THREE.TextureLoader(manager);

三、渲染优化

1. 使用 InstancedMesh 批量渲染

实例化允许同时渲染共享相同几何体和材质的多个对象,通过单次绘制调用即可完成:

javascript

复制代码
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const instancedMesh = new THREE.InstancedMesh(geometry, material, 1000);

for (let i = 0; i < 1000; i++) {
    const matrix = new THREE.Matrix4();
    matrix.setPosition(
        Math.random() * 100 - 50,
        Math.random() * 100 - 50,
        Math.random() * 100 - 50
    );
    instancedMesh.setMatrixAt(i, matrix);
}
instancedMesh.instanceMatrix.needsUpdate = true;
scene.add(instancedMesh);

使用 InstancedMesh 后,1000 个物体的绘制调用从 1000 次减少到 1 次。

2. LOD 层级细节

根据相机距离动态切换模型精度:

javascript

复制代码
const lod = new THREE.LOD();
const highRes = new THREE.Mesh(highResGeometry, material);
const lowRes = new THREE.Mesh(lowResGeometry, material);
lod.addLevel(highRes, 0);   // 近距离使用高精度
lod.addLevel(lowRes, 50);   // 50单位后切换低精度
scene.add(lod);

3. 视锥体裁剪

Three.js 默认启用视锥体裁剪,对于静态对象可手动控制:

javascript

复制代码
mesh.frustumCulled = true;  // 默认开启,自动裁剪视野外物体

四、代码优化建议

  • 使用 BufferGeometry 替代已废弃的 Geometry

  • 合并相似材质的网格,减少 draw call

  • 在动画循环中避免创建新对象,减少垃圾回收压力