레벨 오브 디테일

Level of detail

카메라에서 멀어질수록 눈에 잘 안 보이는 디테일을 단계적으로 줄여, 화면에 안 보이는 폴리곤을 그리는 데 GPU를 쓰지 않는 최적화 기법.

다른 이름: LODTHREE.LOD
···
html
<span id="lod-label" class="lod-label">LOD 0 · high</span>
css
.lod-label{position:absolute;left:0;right:0;bottom:5%;text-align:center;font:600 clamp(9px,2.6vmin,13px)/1 ui-monospace,monospace;color:#dfe3ffdd;letter-spacing:.02em}
js
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0d0d16);
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(0, 0.6, 2.2);

const mat = new THREE.MeshStandardMaterial({ color: 0x8ecbff, roughness: 0.5 });
const lod = new THREE.LOD();
const levels = [
  { geo: new THREE.IcosahedronGeometry(0.9, 4), dist: 0, label: 'LOD 0 · high' },
  { geo: new THREE.IcosahedronGeometry(0.9, 2), dist: 5, label: 'LOD 1 · medium' },
  { geo: new THREE.IcosahedronGeometry(0.9, 0), dist: 9, label: 'LOD 2 · low' },
];
for (const lv of levels) lod.addLevel(new THREE.Mesh(lv.geo, mat), lv.dist);
scene.add(lod);
const key = new THREE.DirectionalLight(0xffffff, 1.8); key.position.set(3, 4, 5); scene.add(key);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));

const label = document.getElementById('lod-label');
function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
camera.lookAt(0, 0, 0);
renderer.setAnimationLoop((t) => {
  const z = -1.5 - (Math.sin(t * 0.00035) * 0.5 + 0.5) * 9.5;
  lod.position.z = z;
  lod.rotation.y = t * 0.0004;
  lod.update(camera);
  const dist = camera.position.distanceTo(lod.position);
  let idx = 0;
  for (let i = levels.length - 1; i >= 0; i--) { if (dist >= levels[i].dist) { idx = i; break; } }
  label.textContent = levels[idx].label;
  renderer.render(scene, camera);
});

카메라에서 3미터 떨어진 캐릭터와 300미터 떨어진 같은 캐릭터는 화면에서 차지하는 픽셀 수가 완전히 다릅니다. 멀리 있는 캐릭터의 미세한 옷 주름까지 매 프레임 계산하는 건 낭비입니다 — 어차피 몇 픽셀로 뭉개져서 안 보입니다. LOD는 같은 오브젝트를 폴리곤 수가 다른 여러 버전(고·중·저)으로 미리 만들어 두고, 거리에 따라 자동으로 바꿔 끼웁니다.

전환이 갑자기 튀어 보이지 않게 두 단계 사이를 서서히 크로스페이드하는(LOD 블렌딩) 경우도 있지만, 기본 동작은 즉시 스왑입니다. 안개나 피사계 심도로 전환 경계를 가려서 눈치채지 못하게 하는 것도 흔한 보완책입니다.

three.js는 THREE.LOD 오브젝트에 여러 해상도의 메시를 거리와 함께 등록해 두면, 카메라 거리를 매 프레임 확인해 알아서 바꿔줍니다. Blender·Maya·게임 엔진(Unity, Unreal)도 같은 개념을 LOD 그룹이나 프록시 메시로 부릅니다 — 지형·군중·숲처럼 개체 수가 많은 장면에서 특히 효과가 큽니다.

언제 쓰나

같은 오브젝트가 화면에 여러 개, 여러 거리로 존재하는 장면(지형·군중·오픈월드)에서 프레임 성능을 지킬 때.