文章目录
- [Three.js 大场景分块加载实战:从全量渲染到可视集调度](#Three.js 大场景分块加载实战:从全量渲染到可视集调度)
Three.js 大场景分块加载实战:从全量渲染到可视集调度
很多 Three.js 项目在 Demo 阶段都很流畅,一到真实业务场景就开始掉帧。根因往往不是"Three.js 不行",而是仍在使用全量渲染思路。
本文给出一套可落地的大场景治理方案:分块、裁剪、调度。
一、为什么全量渲染会崩
当你把园区、工厂、城市一次性丢进场景,问题会立刻出现:
- draw call 暴涨
- 主线程压力高
- 资源下载、解析、上传集中爆发
用户视角一次只看到部分区域,全量加载本质上是在渲染"看不见的成本"。
二、先做空间分块,而不是业务分层
推荐先按空间切块(Grid/Quadtree),每块维护:
- 包围盒(AABB)
- 资源列表(模型、纹理、特效)
- 当前状态(未加载/加载中/已加载)
js
class TileNode {
constructor(id, aabb, assets) {
this.id = id;
this.aabb = aabb;
this.assets = assets;
this.state = 'idle';
}
}
分块后,场景从"一个大对象"变成"可调度的资源单元"。
三、可视集判定:只加载相机附近+可见块
每帧根据相机视锥体和距离,筛出候选块:
js
function collectVisibleTiles(camera, tiles) {
const frustum = new THREE.Frustum();
const proj = new THREE.Matrix4().multiplyMatrices(
camera.projectionMatrix,
camera.matrixWorldInverse
);
frustum.setFromProjectionMatrix(proj);
return tiles.filter(t => frustum.intersectsBox(t.aabb));
}
建议再加距离阈值,避免远处块抢占带宽。
四、加载调度器:限制并发,避免瞬时拥塞
常见错误是可见块一多就并发全开,导致主线程卡死。
工程建议:
- 维护任务队列
- 限制并发(如 2~4)
- 给"近处块"更高优先级
js
const MAX_CONCURRENT = 3;
let running = 0;
async function runTask(task) {
if (running >= MAX_CONCURRENT) return;
running++;
try { await task(); } finally { running--; }
}
五、卸载策略:离开视野要及时回收
只加载不卸载,内存必涨。离相机较远且持续不可见的块,应执行:
- scene.remove
- geometry/material/texture dispose
- 从缓存池标记为可回收
结合 LRU 缓存效果更稳:热点区域保留,冷区逐步淘汰。
六、实践顺序(推荐)
- 先切块(数据结构)
- 再做可视判定
- 上调度器(并发控制)
- 最后做卸载回收
每一步都可独立验证,风险低、收益明确。
七、结语
Three.js 大场景优化不是"魔改引擎",而是建立资源生命周期管理。
当你把"看见什么、加载什么、离开就回收"跑通后,项目稳定性会明显提升。