一、性能瓶颈分析
1.1 常见性能杀手
问题类型 | 典型表现 | 影响范围 |
---|---|---|
Draw Call 爆炸 | 每帧渲染调用超过1000次 | GPU 渲染性能 |
内存占用过高 | 浏览器进程内存突破1GB | 加载速度/崩溃风险 |
模型文件过大 | 单个GLB文件超过50MB | 网络传输时间 |
几何数据冗余 | 重复模型独立加载 | CPU/GPU资源浪费 |
1.2 性能监控工具
// 使用 Stats.js 监控帧率
import Stats from 'three/addons/libs/stats.module.js';
const stats = new Stats();
stats.showPanel(0); // 0: FPS, 1: MS, 2: MB
document.body.appendChild(stats.dom);
function animate() {
stats.begin();
// 渲染逻辑
stats.end();
requestAnimationFrame(animate);
}
二、核心优化方案
2.1 几何合并技术(Geometry Merging)
适用场景:大量相同/相似静态模型
import { BufferGeometryUtils } from 'three/addons/utils/BufferGeometryUtils.js';
// 原始模型加载
const loader = new GLTFLoader();
let geometries = [];
loader.load('tree.glb', (gltf) => {
const baseGeometry = gltf.scene.children[0].geometry;
// 生成1000个不同位置的几何体
for(let i=0; i<1000; i++) {
const geometry = baseGeometry.clone();
geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(
Math.random()*1000 - 500,
0,
Math.random()*1000 - 500
));
geometries.push(geometry);
}
// 合并几何体
const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries);
const material = new THREE.MeshStandardMaterial();
const mergedMesh = new THREE.Mesh(mergedGeometry, material);
scene.add(mergedMesh);
});
优化效果:
-
Draw Calls 从 1000 次降为 1 次
-
内存占用减少约 60%
2.2 实例化渲染(Instanced Mesh)
适用场景:大量重复动态模型
// 创建实例化几何体
const geometry = new THREE.BoxGeometry(1,1,1);
const material = new THREE.MeshPhongMaterial();
const instanceCount = 10000;
const instancedMesh = new THREE.InstancedMesh(geometry, material, instanceCount);
// 设置实例变换矩阵
const matrix = new THREE.Matrix4();
for(let i=0; i<instanceCount; i++) {
matrix.setPosition(
Math.random() * 100 - 50,
Math.random() * 100 - 50,
Math.random() * 100 - 50
);
instancedMesh.setMatrixAt(i, matrix);
}
scene.add(instancedMesh);
性能对比:
方案 | 10000个立方体帧率 |
---|---|
普通 Mesh | 12 FPS |
InstancedMesh | 60 FPS |
2.3 模型压缩与优化
Draco 压缩
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('path/to/draco/');
loader.setDRACOLoader(dracoLoader);
loader.load('model.glb', (gltf) => {
// 压缩后模型处理
});
纹理优化
const textureLoader = new THREE.TextureLoader();
textureLoader.load('texture.jpg', (texture) => {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.magFilter = THREE.LinearFilter; // 关闭默认的mipmap
texture.generateMipmaps = false;
});
压缩效果:
-
模型文件体积减少 60-80%
-
加载时间缩短 40%
三、进阶优化策略
3.1 分帧加载
const modelUrls = [/* 1000个模型路径 */];
let currentIndex = 0;
function loadInBatches() {
for(let i=0; i<5; i++) { // 每帧加载5个
if(currentIndex >= modelUrls.length) return;
loader.load(modelUrls[currentIndex], (gltf) => {
scene.add(gltf.scene);
});
currentIndex++;
}
requestAnimationFrame(loadInBatches);
}
3.2 LOD(细节层次)控制
const lod = new THREE.LOD();
// 高模(距离<50显示)
loader.load('high.glb', (gltf) => {
lod.addLevel(gltf.scene, 50);
});
// 低模(距离>=50显示)
loader.load('low.glb', (gltf) => {
gltf.scene.traverse(child => {
if(child.isMesh) child.geometry = simplifyGeometry(child.geometry);
});
lod.addLevel(gltf.scene, 200);
});
scene.add(lod);
3.3 内存管理
// 模型卸载函数
function disposeModel(mesh) {
mesh.geometry.dispose();
mesh.material.dispose();
scene.remove(mesh);
}
// 视锥裁剪卸载
const frustum = new THREE.Frustum();
const cameraMatrix = new THREE.Matrix4()
.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
function checkVisibility() {
frustum.setFromProjectionMatrix(cameraMatrix);
scene.children.forEach(child => {
if(child.isMesh) {
const inView = frustum.intersectsObject(child);
child.visible = inView;
if(!inView) disposeModel(child);
}
});
}
四、综合优化方案
4.1 推荐技术组合
-
加载阶段:
-
Draco 压缩 + 纹理优化
-
分帧异步加载
-
-
渲染阶段:
-
InstancedMesh + 几何合并
-
LOD + 视锥裁剪
-
-
内存管理:
- 对象池 + LRU缓存策略
4.2 性能对比数据
优化措施 | 加载时间 | 内存占用 | 帧率提升 |
---|---|---|---|
原始方案 | 100% | 100% | 基准 |
Draco压缩 | -65% | -40% | +0% |
几何合并 | -30% | -60% | +300% |
InstancedMesh | -20% | -70% | +400% |
综合优化方案 | -75% | -80% | +450% |
五、调试与监控
5.1 性能面板
// 显示绘制调用次数
const rendererInfo = document.createElement('div');
renderer.domElement.parentElement.appendChild(rendererInfo);
function updateDebugInfo() {
rendererInfo.textContent =
`Draw Calls: ${renderer.info.render.calls}\n` +
`Triangles: ${renderer.info.render.triangles}`;
}
5.2 内存分析
// 打印内存使用情况
console.log(renderer.info.memory);
// 输出示例:
// geometries: 24, textures: 8
通过综合运用这些优化技术,开发者可以在 Three.js 中实现:
-
万级模型流畅加载
-
内存占用降低 80%+
-
60FPS 稳定渲染
实际项目需根据具体场景特点选择合适的优化策略组合,建议通过性能分析工具持续监控优化效果。