Level of Detail

Level of Detail with addLODLevel

Far away, a detailed mesh covers a few pixels at full vertex cost. mesh.addLODLevel(distance, simpler) swaps in a lighter mesh beyond a distance; null stops drawing. A globe for BookNest's reading corner:

A globe with two lighter levels of detailJavaScript
const camera = new BABYLON.ArcRotateCamera('eye', -Math.PI / 2, 1.3, 3,
  new BABYLON.Vector3(0, 1, 0), scene);
const sphere = (name, segments) => BABYLON.MeshBuilder.CreateSphere(name,
  { diameter: 0.4, segments }, scene);
const globe = sphere('globe', 48);
globe.position.y = 1;
globe.addLODLevel(6, sphere('globeMid', 12));             // beyond 6 m
globe.addLODLevel(15, sphere('globeLow', 4));             // beyond 15 m
globe.addLODLevel(30, null);                              // beyond 30 m: not drawn
for (const distance of [3, 10, 20, 40]) {
  camera.radius = distance;
  scene.render();
  const level = globe.getLOD(camera);
  console.log(`${distance} m: ${level ? `${level.name}, ${level.getTotalVertices()} vertices`
    : 'not drawn'}`);
}
Output
3 m: globe, 5151 vertices
10 m: globeMid, 435 vertices
20 m: globeLow, 91 vertices
40 m: not drawn

Levels are separate meshes with their own materials, never drawn on their own. useLODScreenCoverage = true makes the thresholds screen fractions (0 to 1, larger for more detail), independent of scale; instances inherit their source's levels. Build the lighter meshes offline (Blender 4,391 's Decimate, glTF-Transform's simplify).

A reading-corner globe swapping to lighter meshes as the camera pulls away, and vanishing beyond 30 mHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f3ec; font: 13px system-ui, sans-serif; color: #333; }
  canvas { width: 100%; max-width: 600px; height: 300px; display: block; touch-action: none; outline: none; }
  #info { max-width: 600px; padding: 4px 8px; font-family: monospace; }
</style>
<canvas id="view"></canvas>
<div id="info"></div>
<script src="https://cdn.jsdelivr.net/npm/babylonjs@9.28.0/babylon.js"></script>
<script>
const canvas = document.getElementById('view');
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
scene.clearColor = BABYLON.Color4.FromHexString('#f7f3ecff');
new BABYLON.HemisphericLight('sky', new BABYLON.Vector3(0.3, 1, -0.5), scene);
const camera = new BABYLON.ArcRotateCamera('eye', -Math.PI / 2, 1.3, 3, new BABYLON.Vector3(0, 1, 0), scene);
camera.attachControl(canvas, true);
camera.fov = 0.25;                                   // a narrow lens keeps the far globe visible

const sphere = (name, segments, hex) => {
  const mesh = BABYLON.MeshBuilder.CreateSphere(name, { diameter: 0.4, segments }, scene);
  mesh.material = new BABYLON.StandardMaterial(name, scene);
  mesh.material.diffuseColor = BABYLON.Color3.FromHexString(hex);
  mesh.enableEdgesRendering();                       // show the triangles
  mesh.edgesColor = new BABYLON.Color4(0, 0, 0, 0.35);
  return mesh;
};
const globe = sphere('globe', 48, '#1f5f8b');
globe.position.y = 1;
globe.addLODLevel(6, sphere('globeMid', 12, '#3f7d3a'));    // beyond 6 m
globe.addLODLevel(15, sphere('globeLow', 4, '#e09a10'));    // beyond 15 m
globe.addLODLevel(30, null);                         // beyond 30 m: not drawn
const stand = BABYLON.MeshBuilder.CreateCylinder('stand', { height: 0.8, diameterTop: 0.05, diameterBottom: 0.3 }, scene);
stand.position.y = 0.4;

scene.onBeforeRenderObservable.add(() => {           // dolly between 3 m and 40 m
  camera.radius = 21.5 - 18.5 * Math.cos(performance.now() / 2500);
});
scene.onAfterRenderObservable.add(() => {
  const level = globe.getLOD(camera);
  document.getElementById('info').textContent = `${camera.radius.toFixed(1)} m: ` +
    (level ? `${level.name}, ${level.getTotalVertices()} vertices` : 'not drawn');
});
engine.runRenderLoop(() => scene.render());
addEventListener('resize', () => engine.resize());
</script>