Skip to content

Latest commit

 

History

History
141 lines (119 loc) · 3.8 KB

instances.mdx

File metadata and controls

141 lines (119 loc) · 3.8 KB
title sourcecode
Instances
src/core/Instances.tsx
  • A wrapper around THREE.InstancedMesh. This allows you to define hundreds of thousands of objects in a single draw call, but declaratively!

    <Instances
      limit={1000} // Optional: max amount of items (for calculating buffer size)
      range={1000} // Optional: draw-range
    >
      <boxGeometry />
      <meshStandardMaterial />
      <Instance
        color="red"
        scale={2}
        position={[1, 2, 3]}
        rotation={[Math.PI / 3, 0, 0]}
        onClick={onClick} ... />
      // As many as you want, make them conditional, mount/unmount them, lazy load them, etc ...
    </Instances>

    You can nest Instances and use relative coordinates!

    <group position={[1, 2, 3]} rotation={[Math.PI / 2, 0, 0]}>
      <Instance />
    </group>

    Instances can also receive non-instanced objects, for instance annotations!

    <Instance>
      <Html>hello from the dom</Html>
    </Instance>

    You can define events on them!

    <Instance onClick={...} onPointerOver={...} />

    If you need nested, multiple instances in the same parent graph, it would normally not work because an <Instance> is directly paired to its nearest <Instances> provider. You can use the global createInstances helper for such cases, it creates dedicated instances-instance pairs. The first return value is the provider, the second the instance component. Both take the same properties as <Instances> and <Instance>.

    const [CubeInstances, Cube] = createInstances()
    const [SphereInstances, Sphere] = createInstances()
    
    function App() {
      return (
        <>
          <CubeInstances>
            <boxGeometry />
            <meshStandardMaterial />
            <SphereInstances>
              <sphereGeometry />
              <meshLambertMaterial />
              <Cube position={[1, 2, 3]} />
              <Sphere position={[4, 5, 6]} />
            </SphereInstances>
          </CubeInstances>
        </>
      )
    }

    If your custom materials need instanced attributes you can create them using the InstancedAttribute component. It will automatically create the buffer and update it when the component changes. The defaultValue can have any stride, from single floats to arrays.

    <Instances ref={ref} limit={20}>
      <boxGeometry />
      <someSpecialMaterial />
      <InstancedAttribute name="foo" defaultValue={1} />
      <Instance position={[-1.2, 0, 0]} foo={10} />
    </Instances>
    # vertex
    attribute float foo;
    varying float vFoo;
    void main() {
      ...
      vFoo = foo;
    
    # fragment
    varying float vFoo;
    void main() {
      ...

    👉 Note: While creating instances declaratively keeps all the power of components with reduced draw calls, it comes at the cost of CPU overhead. For cases like foliage where you want no CPU overhead with thousands of intances you should use THREE.InstancedMesh such as in this example.

    Typed Instances

    When you need to declare custom attributes for your instances, you can use the createInstances helper to type its attributes.

    interface SphereAttributes {
      myCustomAttribute: number
    }
    
    const [SphereInstances, Sphere] = createInstances<SphereAttributes>()
    
    function App() {
      return (
        <>
          <SphereInstances>
            <InstancedAttribute name="myCustomAttribute" defaultValue={1} />
            <sphereGeometry />
            <shaderMaterial
              // will recienve myCustomAttribute as an attribute
              vertexShader={`
                  attribute float myCustomAttribute;
                  void main() {
                    ...
                  }
                `}
            />
            <Sphere
              position={[4, 5, 6]}
              myCustomAttribute={1} // typed
            />
          </SphereInstances>
        </>
      )
    }