LOD(Level of Detail)是一种3D图形优化技术,其核心思想是:

  • 根据观察者(相机)与物体的距离,动态调整物体的细节级别。距离越远,使用的几何体越简单;距离越近,使用越复杂的几何体。

分类

1. 基于距离的LOD(Distance-based LOD)​​

  • 最经典的实现方式,通过物体与相机的​​绝对距离​​或​​相对距离​​决定使用的模型精度。

  • 原理

    • 预先为物体创建多个细节层级的模型(如LOD0:高模,LOD1:中模,LOD2:低模),并设定每个层级的​​距离阈值​​。当物体与相机的距离进入某个阈值区间时,切换到对应的模型。

  • 示例:

    • 相机距离物体 < 10米:使用高模(LOD0);

    • 10米 ≤ 距离 < 30米:使用中模(LOD1);

    • 距离 ≥ 30米:使用低模(LOD2)。

优缺点:
  • ​优点​​:实现简单,计算成本低(仅需距离判断);

  • ​缺点​​:未考虑物体在屏幕上的实际大小(例如,远处的大物体可能因屏幕占比大而需要更高细节)。

2. 基于屏幕空间的LOD(Screen-space LOD)​​

  • 针对“基于距离”的缺陷优化,通过物体在​​屏幕投影面积​​(或像素占比)决定LOD层级。

  • 原理:

  • 将物体投影到屏幕空间,计算其覆盖的像素数量(或包围盒尺寸)。当投影面积小于某个阈值时,切换到更低精度的模型。

  • 关键步骤:

    • 计算物体在相机视口中的投影包围盒(Bounding Box);

    • 统计包围盒覆盖的像素总数(可通过渲染目标分辨率或相机视锥体计算);

    • 根据像素占比选择LOD层级(如占比 > 1000像素用高模,500-1000用中模,否则用低模)。

优缺点:
  • ​优点​​:更符合人眼感知(屏幕占比大的物体需要更高细节);

  • ​缺点​​:计算成本较高(需每帧计算投影面积),对动态物体需频繁更新。

​​3. 基于视角的LOD(Angle-based LOD)​​

  • 结合观察方向与物体表面的夹角(即​​视角方向与物体法线的夹角​​),优先对正面(法线朝向相机)的物体使用高模,侧面或背面使用低模。

  • 原理:

    • 当物体表面与相机视线的夹角较小时(正面),说明该区域对视觉贡献大,使用高模;反之(侧面/背面)使用低模。

  • 应用场景:

    • 常用于角色模型(如角色的面部正面用高模,后脑勺或身体侧面用低模),或机械结构(如零件的可见面用高模,隐藏面用低模)。

一、CPU简单实现

  1. 基于距离的LOD

const geometry: [THREE.BufferGeometry, number][] = [
    [new THREE.IcosahedronGeometry(100, 30), 50],   // 最高细节,50单位内
    [new THREE.IcosahedronGeometry(100, 8), 300],   // 高细节,300单位内
    [new THREE.IcosahedronGeometry(100, 4), 1000],  // 中等细节,1000单位内
    [new THREE.IcosahedronGeometry(100, 2), 2000],  // 低细节,2000单位内
    [new THREE.IcosahedronGeometry(100, 1), 8000]   // 最低细节,8000单位内
];

这里定义了5个不同细节级别的二十面体:

  • 细分参数:从30到1,数值越大几何体越复杂

  • 切换距离:从50到8000单位,定义了每个级别的有效范围二、GPU实现

完整代码

import { useEffect, useRef } from "react";
import * as THREE from 'three';
import { BufferGeometryUtils, FlyControls, OrbitControls } from "three/examples/jsm/Addons.js";

const LodCpu: React.FC = () => {
    const mountRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        const PARTICLE_SIZE = 20;
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 15000);
        camera.position.z = 1000;

        // 添加雾效增强深度感
        scene.fog = new THREE.Fog(0x000000, 1, 15000);

        // 设置光照
        const pointLight = new THREE.PointLight(0xff2200, 3, 0, 0);
        pointLight.position.set(0, 0, 0);
        scene.add(pointLight);

        const dirLight = new THREE.DirectionalLight(0xffffff, 3);
        dirLight.position.set(0, 0, 1).normalize();
        scene.add(dirLight);

        // 定义LOD几何体层级
        const geometry: [THREE.BufferGeometry, number][] = [
            [new THREE.IcosahedronGeometry(100, 30), 50],
            [new THREE.IcosahedronGeometry(100, 8), 300],
            [new THREE.IcosahedronGeometry(100, 4), 1000],
            [new THREE.IcosahedronGeometry(100, 2), 2000],
            [new THREE.IcosahedronGeometry(100, 1), 8000]
        ];

        const clock = new THREE.Clock();
        const material = new THREE.MeshLambertMaterial({ 
            color: 0xffffff, 
            wireframe: true 
        });

        // 创建多个LOD对象
        for (let j = 0; j < 10; j++) {
            const lod = new THREE.LOD();

            for (let i = 0; i < geometry.length; i++) {
                const mesh = new THREE.Mesh(geometry[i][0] as THREE.BufferGeometry, material);
                mesh.scale.set(1.5, 1.5, 1.5);
                mesh.updateMatrix();
                mesh.matrixAutoUpdate = false;
                lod.addLevel(mesh, geometry[i][1] as number);
            }

            // 随机位置分布
            lod.position.x = 1000 * (0.5 - Math.random());
            lod.position.y = 750 * (0.5 - Math.random());
            lod.position.z = 1000 * (0.5 - Math.random());
            lod.updateMatrix();
            lod.matrixAutoUpdate = false;
            scene.add(lod);
        }

        // 渲染器设置
        let renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        mountRef.current?.appendChild(renderer.domElement);

        // 飞行控制器
        let controls = new FlyControls(camera, renderer.domElement);
        controls.movementSpeed = 1000;
        controls.rollSpeed = Math.PI / 10;

        // 窗口大小调整处理
        const onWindowResize = () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }
        window.addEventListener('resize', onWindowResize);

        // 渲染循环
        const animate = () => {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
            controls.update(clock.getDelta());
        };
        animate();

        return () => {
            renderer.dispose();
        };
    }, []);

    return (
        <div ref={mountRef} style={{ width: '100vw', height: '100vh' }} />
    );
};

export default LodCpu;

二、GPU实现

todo