threlte logo
Geometry

LOD in Threlte

This is a practical example showing a best-practice method of implementing LOD in Threlte. The example is a great demonstration of the power of ref bindings.

This is an adaption of Three.js own documentation, and therefore it’s also a great way to learn how to translate what you already know how to do with imperative three.js, into declerative Threlte code.

<script lang="ts">
	import { Canvas } from '@threlte/core'
	import Scene from './Scene.svelte'
	import { useTweakpane } from '$lib/useTweakpane'

	let reset: (() => void) | undefined

	const { action, addButton } = useTweakpane()

	addButton({
		title: 'Reset Camera',
		onClick: () => {
			reset?.()
		}
	})
</script>

<div use:action />

<Canvas>
	<Scene bind:reset />
</Canvas>
<script>
  import { T, useThrelte } from '@threlte/core'
  import { OrbitControls } from '@threlte/extras'

  let controls
  export const reset = () => controls.reset()
</script>

<T.PerspectiveCamera
  makeDefault
  position={[0,0,25]}
  lookAt.y={0}
>
  <OrbitControls enableZoom={true} bind:ref={controls} />
</T.PerspectiveCamera>

<T.DirectionalLight position={[3, 10, 10]} />
<T.HemisphereLight intensity={0.2} />

<T.LOD let:ref={lod}>
  {#each ["red","green","blue"] as color, i}
    <T.Group on:create={({ref}) => {
      lod.addLevel(ref, i*75)
    }}>
      <T.Mesh>
        <T.IcosahedronGeometry args={[10,3-i]} />
        <T.MeshStandardMaterial {color} wireframe />
      </T.Mesh>
      <T.Mesh>
        <T.IcosahedronGeometry args={[10,3-i]} />
        <T.MeshStandardMaterial {color} transparent opacity={0.3} />
      </T.Mesh>
    </T.Group>
  {/each}
</T.LOD>

How does it work

  1. First <T> creates the geometry and material
  2. Then it attaches those to the mesh
  3. on:create will run later, but we remember to use a reference to the mesh itself ref and a reference lod to the parent LOD object.

… which happens 3 times due to the #each block

<T.LOD let:ref={lod}>
  {#each ["red","green","blue"] as color, i}
    <T.Mesh
      on:create={ ({ref}) => {
        lod.addLevel(ref, i * 75) // i * 75 = distance
      }}
    >
      <T.IcosahedronGeometry args={[10, 3-i]} />
      <T.MeshStandardMaterial {color} wireframe />
    </T.Mesh>
  {/each}
</T.LOD>
  1. <T> now creates the LOD parent and internally calls the three.js function lod.add(child) on each mesh, since they are defined inside the <T.LOD> object.
  2. However, in three.js we need the lod.addLevel(child, distance) as well to register the children as LOD levels and not just attached children.
  3. This is where our on:create function comes in - upon creation of each mesh, we are able to call lod.addLevel(child, distance)