Claude-skill-registry dev-performance-lod-systems

Level of Detail (LOD) techniques for R3F. Use when complex models cause FPS drops.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dev-performance-lod-systems" ~/.claude/skills/majiayu000-claude-skill-registry-dev-performance-lod-systems && rm -rf "$T"
manifest: skills/data/dev-performance-lod-systems/SKILL.md
source content

Level of Detail (LOD)

Reduce polygon count based on distance from camera.

When to Use

Use when:

  • FPS drops on zoom out
  • Complex models in large scenes
  • Mobile devices struggling

Quick Start

import { Detailed } from '@react-three/drei';

function LODTree({ position }) {
  return (
    <Detailed distances={[0, 20, 50, 100]} position={position}>
      {/* Closest - high detail */}
      <HighDetailTree />
      {/* Medium distance */}
      <MediumDetailTree />
      {/* Far - low detail */}
      <LowDetailTree />
      {/* Very far - billboard */}
      <mesh>
        <planeGeometry args={[1, 2]} />
        <meshBasicMaterial map={treeBillboard} transparent />
      </mesh>
    </Detailed>
  );
}

Distance Guidelines

DistanceDetail LevelPolygonsUse Case
0-10mHigh10,000+Close inspection
10-30mMedium1,000-5,000Normal gameplay
30-60mLow100-500Background
60m+Billboard2 trianglesDistant objects

LOD Component Models

// High detail - close inspection only
function HighDetailTree() {
  return (
    <group>
      <mesh>
        {/* Full trunk with bark texture */}
        <cylinderGeometry args={[0.3, 0.5, 4, 16]} />
        <meshStandardMaterial map={barkTexture} />
      </mesh>
      <mesh position={[0, 2, 0]}>
        {/* Dense foliage */}
        <coneGeometry args={[2, 4, 8]} />
        <meshStandardMaterial map={leafTexture} />
      </mesh>
    </group>
  );
}

// Medium detail - gameplay distance
function MediumDetailTree() {
  return (
    <group>
      <mesh>
        {/* Simplified trunk */}
        <cylinderGeometry args={[0.25, 0.4, 4, 8]} />
        <meshStandardMaterial color="brown" />
      </mesh>
      <mesh position={[0, 2, 0]}>
        {/* Simplified foliage */}
        <coneGeometry args={[1.8, 4, 6]} />
        <meshStandardMaterial color="green" />
      </mesh>
    </group>
  );
}

// Low detail - background
function LowDetailTree() {
  return (
    <mesh>
      {/* Single merged geometry */}
      <cylinderGeometry args={[0.2, 1.5, 4, 6]} />
      <meshStandardMaterial color="darkgreen" />
    </mesh>
  );
}

// Billboard - very distant
function BillboardTree() {
  return (
    <mesh>
      <planeGeometry args={[1, 3]} />
      <meshBasicMaterial map={treeBillboard} transparent />
    </mesh>
  );
}

Automatic LOD Generation

import { useGLTF } from '@react-three/drei';

function AutoLOD({ modelPath }) {
  const { scene: highDetail } = useGLTF(`${modelPath}_high.glb`);
  const { scene: mediumDetail } = useGLTF(`${modelPath}_medium.glb`);
  const { scene: lowDetail } = useGLTF(`${modelPath}_low.glb`);

  return (
    <Detailed distances={[0, 15, 40]}>
      <primitive object={highDetail.clone()} />
      <primitive object={mediumDetail.clone()} />
      <primitive object={lowDetail.clone()} />
    </Detailed>
  );
}

LOD with Skeletal Animation

// Animated characters need special handling
function LODCharacter() {
  // High detail: full rig, many bones
  // Low detail: simplified rig, fewer bones
  // LODs must share animation structure

  const highDetail = useGLTF('/character_high.glb');
  const lowDetail = useGLTF('/character_low.glb');

  // Clone animations to both models
  const animations = highDetail.animations;

  return (
    <Detailed distances={[0, 25]}>
      <primitive object={highDetail.scene} animations={animations} />
      <primitive object={lowDetail.scene} animations={animations} />
    </Detailed>
  );
}

Billboard Optimization

// Use billboards for very distant objects
function BillboardSprite({ texture, position }) {
  const mesh = useRef<THREE.Mesh>(null);

  useFrame(({ camera }) => {
    if (mesh.current) {
      // Always face camera
      mesh.current.quaternion.copy(camera.quaternion);
    }
  });

  return (
    <mesh ref={mesh} position={position}>
      <planeGeometry args={[2, 2]} />
      <meshBasicMaterial map={texture} transparent />
    </mesh>
  );
}

Common Mistakes

❌ Wrong✅ Right
Too many LOD levels (>4)3-4 levels is sufficient
Abrupt distance transitionsSmooth distances (overlap)
No billboard for distant objectsUse 2-triangle billboards
Creating LODs manuallyUse tools like SimplyGLTF

LOD Creation Tools

  • SimplyGLTF - Online LOD generator
  • Blender - Manual decimation modifier
  • meshoptimizer - Command-line tool

Reference