【ThreeJs】【性能优化】从渲染底层到业务逻辑的系统性提速方案
⚙️ Three.js 性能优化全攻略
—— 从渲染底层到业务逻辑的系统性提速方案
🎯 一、核心目标
优化 Three.js 的最终目的只有三件事:
目标 | 说明 |
---|---|
🧠 减少计算 | CPU 少做无用功 |
💡 减少绘制 | GPU 少画不必要的像素 |
⚡ 提升流畅 | FPS 稳定在 60+ |
🧱 二、渲染层优化(GPU 友好型)
1️⃣ 合并模型(Draw Call 优化)
Draw Call 越多,渲染越慢。每一个独立的 Mesh 都是一条 Draw Call。
优化方案:
- 使用
BufferGeometryUtils.mergeGeometries()
合并同材质模型; - 多个相同物体使用 InstancedMesh(一次绘制多份);
- 尽量复用材质对象。
import { InstancedMesh } from "three";const mesh = new InstancedMesh(geometry, material, 1000);
scene.add(mesh);
🧩 场景示例:公寓 3D 看房中有很多相同家具(椅子、灯),用 InstancedMesh 性能提升数倍。
2️⃣ 材质与贴图优化
优化点 | 建议 |
---|---|
贴图尺寸 | 控制在 2k 以下(2048×2048),一般 1k 足够 |
压缩格式 | 使用 WebP / Basis / KTX2 |
重复贴图 | 复用相同材质,避免重复加载 |
环境贴图 | 预先预处理为 PMREM,减少实时计算 |
💡 小技巧:
texture.encoding = THREE.sRGBEncoding;
可提升色彩质量,同时保持 GPU 负担轻。
3️⃣ 灯光优化
灯光类型 | 开销等级 | 建议 |
---|---|---|
PointLight | 🔥 高 | 谨慎使用,多用贴图假光 |
SpotLight | 🔥🔥 高 | 少用,多采用烘焙光源 |
DirectionalLight | ⚡ 中 | 常用主光源 |
HemisphereLight | 💨 轻 | 用作环境补光 |
🚀 最优方案:
使用 烘焙贴图(Lightmap) 或 环境贴图 替代动态光源。
在Three.js等3D图形开发环境中,表格里提到的几种灯光类型含义如下:
PointLight(点光源)
- 含义:从一个点向四面八方均匀地发射光线,类似现实生活中的灯泡 。点光源的光照强度会随着距离的增加而衰减。
- 应用场景:常用于模拟室内的灯泡、蜡烛等光源效果,比如在一个室内场景中模拟吊灯发出的光线,照亮周围的物体。但由于它向各个方向发射光线,计算量较大,开销等级高, 所以如果场景中有大量点光源,会对性能产生较大影响,因此建议谨慎使用,或者可以考虑使用贴图模拟假光来减少真实光源的使用。
SpotLight(聚光灯)
- 含义:光线从一个点出发,按照一个特定的圆锥体范围向外照射,类似现实生活中的手电筒、舞台聚光灯 。可以设置聚光灯的照射角度、衰减等属性。
- 应用场景:适用于需要突出某个特定区域的场景,比如在舞台场景中模拟照亮演员的聚光灯,或者在游戏中模拟手电筒照亮前方的一小片区域。然而,由于其复杂的光照计算(要考虑圆锥体范围内的光照以及相关衰减等),开销等级很高, 所以在项目中要尽量少用,对于一些固定的光照效果,可以采用烘焙光源的方式,提前计算好光照结果,减少实时计算量。
DirectionalLight(平行光)
- 含义:可以理解为光线从无限远的地方射来,所有的光线都是平行的,不会产生衰减。类似现实生活中的太阳光,无论距离多远,物体接收到的光照强度基本相同。
- 应用场景:是场景中常用的主光源,用来模拟太阳光等大面积均匀的光照,比如在一个室外场景中,使用平行光作为太阳光,照亮整个场景中的物体。它的开销等级处于中等水平,在性能和光照效果之间能取得较好的平衡。
HemisphereLight(半球光)
- 含义:有两个颜色,一个代表天空的颜色,一个代表地面的颜色,光线从天空方向向地面方向照射,模拟自然环境中大气散射等环境光效果 。
- 应用场景:主要用于为场景提供柔和的环境补光,提升场景整体的亮度和氛围,让场景看起来更加自然。由于它的光照计算相对简单,开销等级轻, 可以在不影响太多性能的情况下,改善场景的视觉效果。
4️⃣ 阴影优化
阴影是 GPU 杀手 ⚠️
优化技巧 | 效果 |
---|---|
减小阴影贴图尺寸 | light.shadow.mapSize.set(1024, 1024) |
限制阴影范围 | 调整 camera.near / camera.far |
部分对象禁用阴影 | mesh.castShadow = false |
使用假阴影贴图 | 平面 + 半透明纹理 |
🧠 三、逻辑层优化(CPU 层面)
1️⃣ 控制刷新频率
不要在 animate()
每帧都做复杂逻辑。
✅ 把不需要每帧执行的操作放到定时器或事件中。
if (clock.getElapsedTime() - lastUpdate > 0.2) {updateUI();lastUpdate = clock.getElapsedTime();
}
2️⃣ 使用节流与防抖
尤其是窗口 resize、鼠标事件:
window.addEventListener("resize", debounce(onResize, 200));
3️⃣ 使用惰性加载(懒加载)
按需加载模型与贴图,不要一次性全加载:
if (camera.position.distanceTo(targetRoom) < 50) {loadRoomAssets();
}
💡 四、几何与模型优化
优化项 | 说明 |
---|---|
🪶 降低顶点数 | 模型导出前简化面数 |
🧩 使用 glTF 压缩 | Draco 或 Meshopt 压缩 |
📦 缓存模型 | 加载一次后存入缓存,下次直接复用 |
🌀 LOD(多层细节) | 远距离用低模,近距离换高模 |
const lod = new THREE.LOD();
lod.addLevel(highPolyMesh, 0);
lod.addLevel(lowPolyMesh, 50);
scene.add(lod);
🧭 五、渲染管线优化
✅ Renderer 设置
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.physicallyCorrectLights = true;
- 限制
pixelRatio
,否则高分屏会爆显存; - 启用 sRGB,提升画质;
- 不开启抗锯齿时性能更高。
⚡ 后期特效优化
后期(PostProcessing)链条越短越好。
- 合并多个 Pass;
- 使用 FXAA 替代 SMAA;
- 仅在拍照或静态模式启用特效。
🌍 六、场景管理优化
- 场景分层加载(当前房间 + 邻近房间);
- 离开房间时清理材质、几何体;
- 使用
dispose()
手动释放显存:
geometry.dispose();
material.dispose();
texture.dispose();
🧩 七、监控与调试工具
工具 | 功能 |
---|---|
stats.js | 查看 FPS、渲染帧时间 |
three-inspector | 实时查看场景结构 |
WebGL Profiler (Chrome) | 分析 GPU 绘制耗时 |
spector.js | 深度分析每帧渲染调用 |
✅ 八、实战优化策略总结
分类 | 优化手段 | 提升 |
---|---|---|
GPU 绘制 | 合并模型、压缩贴图、少灯光 | ⚡⚡⚡ |
CPU 运算 | 节流逻辑、懒加载 | ⚡⚡ |
模型数据 | 降面、LOD、压缩 | ⚡⚡ |
渲染配置 | 降分辨率、合理后期 | ⚡ |
内存管理 | 及时 dispose | ⚡⚡ |
🏁 九、真实案例:3D 看房项目提速记录
优化阶段 | FPS 提升 |
---|---|
原始版本(未优化) | 25 FPS |
材质压缩 + 模型合并 | 40 FPS |
阴影简化 + LOD | 55 FPS |
懒加载 + 控制刷新 | 稳定 60 FPS |
优化是一场「减法艺术」——
少算一点,少画一点,性能自然飞起来 🚀。