在Three.js的世界里,炫酷特效固然吸引眼球,但性能优化才是保证用户体验的王道。想象一下:你的3D场景卡成PPT,用户手机发烫能煎蛋,这体验绝对劝退。今天咱们就聊聊那些让Three.js飞起来的实战技巧!
优化核心思路:少干活,干聪明活
技巧1:几何体合并 - 减少Draw Call
每次绘制调用(Draw Call)都是性能杀手。当场景有5000个立方体时,合并成一个几何体,Draw Call就从5000降到1!
// 创建合并几何体
const mergeGeometry = new THREE.BufferGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// 生成5000个立方体位置
const count = 5000;
const positions = new Float32Array(count * 3); // 每个立方体3个坐标值
for (let i = 0; i < count * 3; i += 3) {
positions[i] = (Math.random() - 0.5) * 200; // x
positions[i + 1] = (Math.random() - 0.5) * 200; // y
positions[i + 2] = (Math.random() - 0.5) * 200; // z
}
// 设置几何体属性
mergeGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const points = new THREE.Points(mergeGeometry, material);
scene.add(points);
技巧2:实例化渲染 - 动态物体的救星
需要移动的物体怎么办?InstancedMesh来帮忙!它只上传一次几何体数据,通过变换矩阵批量渲染。
// 创建实例化网格
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const instances = 1000;
const instancedMesh = new THREE.InstancedMesh(geometry, material, instances);
// 设置每个实例位置
const matrix = new THREE.Matrix4();
for (let i = 0; i < instances; i++) {
matrix.setPosition(
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100
);
instancedMesh.setMatrixAt(i, matrix);
}
scene.add(instancedMesh);
// 旋转动画(高效!)
function animate() {
requestAnimationFrame(animate);
instancedMesh.rotation.x += 0.01;
renderer.render(scene, camera);
}
animate();
技巧3:纹理优化 - 别让高清图拖垮GPU
4K纹理虽好,但小屏幕上纯属浪费!试试这些技巧:
- 使用
CompressedTextureLoader加载压缩纹理 - 动态调整分辨率:
texture.generateMipmaps = true - 离屏渲染用
Math.floor(size / 2)逐级缩小
// 智能加载纹理
const textureLoader = new THREE.TextureLoader();
textureLoader.load('texture.jpg', texture => {
// 根据设备调整尺寸
const maxSize = Math.min(1024, renderer.capabilities.maxTextureSize);
texture.image.width = Math.min(texture.image.width, maxSize);
texture.image.height = Math.min(texture.image.height, maxSize);
texture.needsUpdate = true;
});
技巧4:LOD(细节分级) - 远观勿近玩
离摄像机远的物体,用低模就够了!Three.js的LOD系统自动切换模型精度。
// 创建LOD对象
const lod = new THREE.LOD();
// 添加不同距离的模型
const highRes = loadModel('high.glb'); // 精细模型
const lowRes = loadModel('low.glb'); // 简化模型
lod.addLevel(highRes, 50); // 50像素内显示精细模型
lod.addLevel(lowRes, 200); // 200像素外显示简化模型
scene.add(lod);
// 在渲染循环中更新
function updateLOD() {
lod.update(camera);
}
技巧5:渲染策略 - 看不见就别画
视锥剔除:只渲染摄像机可见范围内的物体(Three.js默认开启)
遮挡剔除:用OcclusionCuller跳过被遮挡的物体
按需渲染:静态场景用renderer.setAnimationLoop(null)停止循环渲染
// 智能渲染控制
let needsUpdate = false;
// 只有场景变化时才渲染
function checkRender() {
if (needsUpdate) {
renderer.render(scene, camera);
needsUpdate = false;
}
requestAnimationFrame(checkRender);
}
// 当物体移动时标记更新
object.position.x += 0.1;
needsUpdate = true;
性能检测神器
动手前先诊断性能瓶颈:
// 显示性能面板
import Stats from 'stats.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();
}
// 查看Draw Call数量
console.log(renderer.info.render.calls);