Skip to content

LOD 多层次细节

本文详细介绍 Three.js 中 LOD(Level of Detail,多层次细节)的原理、API 用法以及实际应用场景。通过动态切换不同精度的模型,在保证视觉质量的同时大幅提升渲染性能。

一、什么是 LOD?

LOD(Level of Detail,多层次细节) 是 3D 渲染中一项经典的性能优化技术。核心思想非常简单:根据物体与相机之间的距离,动态切换不同精度的模型

  • 近距离:使用高精度模型(面数多、细节丰富)
  • 中距离:使用中等精度模型(面数减少、保留关键轮廓)
  • 远距离:使用低精度模型(面数最少、仅保留大致形状)

这背后的逻辑很直观——当物体距离相机很远时,它在屏幕上只占几个像素,使用高精度模型完全是浪费 GPU 资源。

二、为什么需要 LOD?

2.1 性能收益

假设场景中有 100 个相同的角色模型,每个模型的高精度版本有 10,000 个三角面。如果全部使用高精度模型:

  • 总三角面数:100 × 10,000 = 1,000,000 面
  • 大部分远处的角色在屏幕上只占几十个像素,这 10,000 面的细节肉眼完全无法分辨

引入 LOD 后,假设:

  • 近处 10 个使用高精度(10,000 面)

  • 中间 30 个使用中精度(3,000 面)

  • 远处 60 个使用低精度(500 面)

  • 总三角面数:10×10,000 + 30×3,000 + 60×500 = 220,000 面

  • 相比全部使用高精度,面数减少了 78%,但视觉上几乎没有差异

2.2 适用场景

场景类型说明
开放世界游戏大量远景物体(树木、建筑、NPC)需要 LOD
城市可视化远距离建筑可以大幅简化
VR/AR 应用对帧率要求极高(90fps+),LOD 是必需品
粒子替代远景粒子系统可用低面数几何体代替

三、THREE.LOD 类详解

Three.js 提供了内置的 THREE.LOD 类来实现多层次细节。它的用法非常简单——你只需要告诉它"在什么距离显示什么模型"即可。

3.1 基本用法

typescript
import * as THREE from 'three';

// 创建 LOD 对象
const lod = new THREE.LOD();

// 创建三个不同精度的模型
const highDetail = new THREE.Mesh(
  new THREE.SphereGeometry(1, 64, 64),    // 高精度:64段
  new THREE.MeshStandardMaterial({ color: 0xff0000 })
);

const mediumDetail = new THREE.Mesh(
  new THREE.SphereGeometry(1, 32, 32),    // 中精度:32段
  new THREE.MeshStandardMaterial({ color: 0xff0000 })
);

const lowDetail = new THREE.Mesh(
  new THREE.SphereGeometry(1, 8, 8),      // 低精度:8段
  new THREE.MeshStandardMaterial({ color: 0xff0000 })
);

// 添加 LOD 层级:模型 + 切换距离
lod.addLevel(highDetail, 0);      // 距离 0-20 时显示高精度
lod.addLevel(mediumDetail, 20);   // 距离 20-50 时显示中精度
lod.addLevel(lowDetail, 50);      // 距离 >50 时显示低精度

// 添加到场景
scene.add(lod);

3.2 API 参考

addLevel(object, distance)

向 LOD 中添加一个细节层级。

  • object:该层级显示的 Object3D(可以是 Mesh、Group 等任意 3D 对象)
  • distance:切换到此层级的距离阈值,即相机到 LOD 对象世界坐标的欧几里得距离(camera.position.distanceTo(lod.position)

重要:距离阈值的顺序很重要!addLevel 会自动按照距离从小到大排序。距离值相同时,后添加的层级会被忽略。

typescript
// 推荐:按距离从小到大添加
lod.addLevel(highDetail, 0);
lod.addLevel(mediumDetail, 30);
lod.addLevel(lowDetail, 60);

// 不推荐但也能工作——LOD 会自动排序
lod.addLevel(lowDetail, 60);
lod.addLevel(highDetail, 0);
lod.addLevel(mediumDetail, 30);

getObjectForDistance(distance)

获取指定距离下应该显示的对象。返回当前相机距离对应的那个模型。

typescript
const obj = lod.getObjectForDistance(25);
console.log(obj); // mediumDetail(如果 25 在 [30, 60) 范围内)

属性:levels

存储所有层级的数组,每个元素包含 { distance, object }

typescript
console.log(lod.levels);
// [
//   { distance: 0, object: highDetail },
//   { distance: 30, object: mediumDetail },
//   { distance: 60, object: lowDetail },
// ]

自动更新机制

THREE.LOD 的切换是全自动的——不需要手动调用任何方法。它依赖渲染器在每帧渲染前自动计算相机到 LOD 对象之间的距离,然后选择对应的层级。

自动更新的条件是 lod.autoUpdate = true(默认值)。如果你需要手动控制切换时机,可以将其设为 false

3.3 层级对象的选择

每个 LOD 层级不仅可以是一个简单的 Mesh,还可以是:

typescript
// 1. 单个 Mesh
lod.addLevel(mesh, 0);

// 2. Group(包含多个子对象)
const group = new THREE.Group();
group.add(mesh1, mesh2, mesh3);
lod.addLevel(group, 30);

// 3. 另一个 LOD 对象(嵌套 LOD)
const subLod = new THREE.LOD();
lod.addLevel(subLod, 60);

// 4. InstancedMesh(批量渲染)
const instanced = new THREE.InstancedMesh(geometry, material, count);
lod.addLevel(instanced, 100);

四、完整示例

我们创建了一个完整的 LOD 示例,可以在 示例页面 在线体验。

4.1 场景设计

在场景中放置了 5 组不同颜色的 LOD 对象,每组包含三个细节层级:

层级几何体面数切换距离
高精度TorusKnotGeometry (100×16段)~3,200 面0 - 30
中精度TorusKnotGeometry (48×8段)~768 面30 - 60
低精度OctahedronGeometry (1级细分)~8 面> 60

同时,每个层级添加了半透明线框,方便观察几何体结构的变化。

4.2 核心代码解析

typescript
export default class World extends THREE.Object3D {
  lods: THREE.LOD[] = [];

  initScene() {
    // 添加光照和地面参考网格
    const ambient = new THREE.AmbientLight(0xffffff, 0.4);
    const dir = new THREE.DirectionalLight(0xffffff, 1.5);
    dir.position.set(50, 80, 40);
    this.add(ambient, dir);

    const grid = new THREE.GridHelper(200, 40, 0x888888, 0x444444);
    this.add(grid);

    // 在不同位置放置 LOD 对象
    const positions = [
      { x: -30, z: -30, color: 0xff6b6b },
      { x:  30, z: -30, color: 0x6bc5ff },
      { x: -30, z:  30, color: 0xffd93d },
      { x:  30, z:  30, color: 0x6bff6b },
      { x:   0, z:   0, color: 0xff6bff },
    ];

    positions.forEach(({ x, z, color }) => {
      const lod = this.createLODObject(color);
      lod.position.set(x, 0, z);
      this.add(lod);
      this.lods.push(lod);
    });
  }

  createLODObject(color: number): THREE.LOD {
    const lod = new THREE.LOD();

    // 高精度层级(0 - 30)
    const highDetail = new THREE.Group();
    const torusKnot = new THREE.Mesh(
      new THREE.TorusKnotGeometry(3, 0.8, 100, 16),
      new THREE.MeshStandardMaterial({
        color, metalness: 0.3, roughness: 0.4,
      })
    );
    highDetail.add(torusKnot);
    // 额外装饰球体——只在近距离可见
    const sphere = new THREE.Mesh(
      new THREE.SphereGeometry(1.2, 32, 32),
      new THREE.MeshStandardMaterial({
        color: 0xffffff, emissive: color, emissiveIntensity: 0.3,
      })
    );
    sphere.position.set(4, 3, 0);
    highDetail.add(sphere);

    // 中精度层级(30 - 60)
    const mediumDetail = new THREE.Mesh(
      new THREE.TorusKnotGeometry(3, 0.8, 48, 8),
      new THREE.MeshStandardMaterial({ color, metalness: 0.3, roughness: 0.4 })
    );

    // 低精度层级(> 60)
    const lowDetail = new THREE.Mesh(
      new THREE.OctahedronGeometry(4, 1),
      new THREE.MeshStandardMaterial({ color, metalness: 0.3, roughness: 0.5 })
    );

    lod.addLevel(highDetail, 0);
    lod.addLevel(mediumDetail, 30);
    lod.addLevel(lowDetail, 60);

    return lod;
  }
}

4.3 如何观察 LOD 切换效果

打开示例页面后:

  1. 滚轮缩放到远处——可以看到对象从圆环结切换到八面体(低精度)
  2. 滚轮拉近——八面体又切换回圆环结(高精度),并且出现装饰小球
  3. 拖动旋转视角——可以从不同角度观察 LOD 的变化

场景中地面上有 Sprite 标签标注了三个距离区间,帮助你更直观地理解切换边界。

4.4 线框辅助观察

代码中为每个 LOD 层级添加了半透明线框,这让我们可以直接"看到"面数的变化:

typescript
// 高精度线框:密密麻麻的网格
const wireframeHigh = new THREE.LineSegments(
  new THREE.WireframeGeometry(new THREE.TorusKnotGeometry(3, 0.8, 100, 16)),
  new THREE.LineBasicMaterial({ color: 0x000000, transparent: true, opacity: 0.1 })
);
highDetail.add(wireframeHigh);

// 低精度线框:寥寥几条线
const wireframeLow = new THREE.LineSegments(
  new THREE.WireframeGeometry(new THREE.OctahedronGeometry(4, 1)),
  new THREE.LineBasicMaterial({ color: 0x000000, transparent: true, opacity: 0.1 })
);
lowDetail.add(wireframeLow);

拉远观察时,你可以清楚地看到线框从密集变为稀疏——这就是 LOD 帮你节省的 GPU 算力。

五、进阶技巧

5.1 与 InstancedMesh 结合

对于大量重复物体(如森林中的树木),LOD + InstancedMesh 是最强组合:

typescript
// 远距离:使用 InstancedMesh 批量渲染低精度几何体
const farGeometry = new THREE.ConeGeometry(1, 4, 4, 2);
const farMaterial = new THREE.MeshStandardMaterial({ color: 0x228b22 });
const farTrees = new THREE.InstancedMesh(farGeometry, farMaterial, 1000);

// 近距离:独立的高精度 Mesh
const nearGeometry = new THREE.CylinderGeometry(0.5, 0.8, 5, 16, 8);

// 按距离分配
trees.forEach((tree, i) => {
  const dist = tree.position.distanceTo(camera.position);
  if (dist < 50) {
    // 替换为高精度 Mesh
    scene.add(createHighDetailTree(tree.position));
  }
});
// 剩余远距离的树由 InstancedMesh 统一渲染

5.2 平滑过渡

原生的 LOD 切换是"硬切换"(瞬间替换),可能产生视觉跳变。如果需要平滑过渡,可以结合透明度:

typescript
// 在两个层级之间做透明度渐变
function smoothLODTransition(lod: THREE.LOD, camera: THREE.Camera) {
  const dist = camera.position.distanceTo(lod.position);

  if (dist > 25 && dist < 35) {
    // 过渡区间:两个层级同时显示,通过透明度混合
    const t = (dist - 25) / 10; // 0 到 1
    const highObj = lod.levels[0].object as THREE.Mesh;
    const medObj = lod.levels[1].object as THREE.Mesh;

    highObj.material.opacity = 1 - t;
    medObj.material.opacity = t;
  }
}

注意:这需要材质开启 transparent: true,并且需要将 autoUpdate 设为 false 后手动管理层级切换,实现复杂度较高。

5.3 LOD 距离的动态调整

不同性能档位的设备可以使用不同的 LOD 切换距离:

typescript
const isMobile = /Mobi|Android/i.test(navigator.userAgent);
const distanceMultiplier = isMobile ? 0.6 : 1.0; // 移动端提前切换到低精度

lod.addLevel(highDetail, 0);
lod.addLevel(mediumDetail, 30 * distanceMultiplier);
lod.addLevel(lowDetail, 60 * distanceMultiplier);

六、注意事项

  1. 距离计算基于 LOD 对象的位置,不是相机的世界坐标。确保 LOD 对象的位置正确设置。

  2. 层级对象会被自动切换显隐visible 属性),LOD 内部通过设置 object.visible 来控制显示哪个层级。

  3. 材质共享:不同层级可以共享同一个材质实例,但要注意如果需要为不同层级设置不同的材质属性,则需要分别创建。

  4. 不要过度使用 LOD:如果场景中物体很少,LOD 的收益有限。通常建议在物体数量 > 20 且存在明显距离差异时才引入 LOD。

  5. 几何体的选择:低精度几何体不需要和原始模型形状完全一致,只要在远处看起来"差不多"即可。八面体、二十面体、低段数球体都是很好的低精度替代。

  6. LOD 在 renderer.render() 时自动更新,确保在渲染前 LOD 对象已经被添加到场景中。

七、总结

维度要点
核心思想距离越远,模型越简单
Three.js APInew THREE.LOD() + lod.addLevel(object, distance)
自动化程度完全自动,无需手动控制切换
性能收益可减少 50%-90% 的三角面数(取决于场景规模)
最佳搭档InstancedMesh(远距离)+ LOD(多精度层级)
适用场景大场景、开放世界、城市可视、VR/AR

LOD 是 3D 性能优化的基础手段之一,Three.js 提供的 THREE.LOD 类让这一技术的使用门槛降到了最低。只需要定义好不同层级的模型和切换距离,引擎就会在运行时自动帮你完成一切。

推荐阅读: