Three.js性能优化实战:让3D丝滑如德芙

1,145 阅读2分钟

在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);