@three.ez/instanced-mesh Documentation

repository·master·Indexed 19 days ago

https://github.com/agargaro/instanced-mesh

Enhanced version of Three.js's InstancedMesh (InstancedMesh2) featuring dynamic capacity management, per-instance transforms via InstancedEntity, and spatial indexing using BVH for fast raycasting and frustum culling. Supports per-instance visibility, opacity, shader uniforms, Level of Detail (LOD), skeletal animation (skinning), and sorting for transparency.

Tokens
16.7K
Snippets
65
Records
81
Agent score
65%

What's inside @three.ez/instanced-mesh

  1. Overview of InstancedMesh2 features

    master

    InstancedMesh2 is an enhanced implementation of Three.js InstancedMesh designed to provide advanced features for managing large numbers of instances efficiently. Key capabilities include:

    • Frustum Culling: Per-instance frustum culling to improve rendering performance.
    • Fast Raycasting: High-performance raycasting using a Bounding Volume Hierarchy (BVH).
    • Dynamic Capacity: Ability to manage instance counts dynamically.
    • Object3D-like Instances: Provides an interface for instances that behaves similarly to standard Object3D objects.
    • Spatial Indexing: Uses a dynamic BVH for spatial queries.
    • Advanced Rendering Controls: Supports sorting, per-instance visibility, Level of Detail (LOD), and skinning.
  2. What is InstancedMesh2?

    master

    InstancedMesh2 is an enhanced alternative to the standard Three.js InstancedMesh. It is designed for high-performance rendering of large numbers of instances by providing advanced features that the standard implementation lacks.

    Key features include:

    • Per-instance frustum culling: Skips rendering for instances that are out of view.
    • Sorting: Reduces overdraw and manages transparent objects efficiently.
    • Spatial indexing (dynamic BVH): Speeds up raycasting and frustum culling.
    • Dynamic capacity: Allows for seamless adding or removing of instances.
    • Object3D-like instances: Instances behave like Object3D, supporting transforms and custom data.
    • Per-instance control: Individual visibility, opacity, and custom shader uniforms.
    • LOD (Level of Detail): Supports both geometry LOD and Shadow LOD based on distance.
    • Skinning: Supports skeletal animations for instances.
  3. Use Object3D-like instances with InstancedEntity

    master

    By setting createEntities: true in the constructor, you can access an array of InstancedEntity objects. These entities behave like Object3D, allowing you to manipulate transforms (position, rotation, scale) and custom data directly.

    Important: You must call .updateMatrix() after performing transformations to apply changes to the instance.

    const myInstancedMesh = new InstancedMesh2(geometry, material, { createEntities: true });
    
    myInstancedMesh.instances[0].customData = {};
    myInstancedMesh.instances[0].position.random();
    myInstancedMesh.instances[0].rotateX(Math.PI);
    myInstancedMesh.instances[0].updateMatrix(); // necessary after transformations
  4. How frustum culling algorithms work

    master

    The library uses two different methods for performing frustum culling every frame:

    1. Linear (default): Iterates through every instance and checks if its boundingSphere is inside the camera's frustum. This is best for dynamic scenarios. Note that linear culling works more efficiently if the geometry is centered.
    2. BVH: If a Bounding Volume Hierarchy (BVH) has been built, the algorithm recursively iterates through the nodes. If a node is outside the frustum, the entire node and its children are discarded. This is best for mostly static scenarios.
  5. Compare InstancedMesh vs InstancedMesh2

    master

    Choosing between the standard Three.js InstancedMesh and InstancedMesh2 depends on your performance needs and feature requirements.

    InstancedMesh (Standard)

    • Data Storage: Uses InstancedBufferAttribute for matrices and data.
    • Rendering: Renders instances sequentially in the order they were added. It cannot skip off-screen instances or reorder them for transparency.
    • Best Use Case: When you do not need culling, sorting, or dynamic capacity, and want to avoid the minor overhead of indirection.

    InstancedMesh2

    • Data Storage: Uses SquareDataTexture (an extended DataTexture supporting partial updates) to store matrices and data.
    • Rendering: Uses an InstancedBufferAttribute to manage instance indexes. This allows for selective rendering, efficient culling, and sorting before data is sent to the GPU.
    • Performance Trade-off: Rendering all instances is slightly slower than standard InstancedMesh due to the additional indirection. However, the performance gains from efficient culling and sorting typically outweigh this overhead in complex scenes.
  6. How raycasting works in InstancedMesh

    master

    Raycasting in this library can be performed using two different strategies depending on your performance needs and scene dynamics:

    1. Linear (Default): Iterates through every instance and calls the standard Three.js raycast method on each. This is the best approach for dynamic scenarios where instances move frequently.
    2. BVH (Bounding Volume Hierarchy): If a BVH has been built for the mesh, the raycaster recursively iterates through the BVH nodes. If a ray does not intersect a node, that node and all its children are discarded from the search. This is the best approach for mostly static scenarios.

    Optimization Tip: If you are using complex geometry, it is recommended to use three-mesh-bvh to accelerate the raycasting of the underlying mesh by creating a BVH containing the triangles.

  7. Animate a single instance

    master

    To update a specific instance, access it via the instances array of your InstancedMesh. After modifying properties like position, scale, or quaternion, you must call .updateMatrix() to apply the changes to the instance's transformation.

    Performance Tip: If you are only modifying the position property, use .updateMatrixPosition() instead of .updateMatrix() to improve performance.

    iMesh.instances[index].position.random();
    iMesh.instances[index].rotateX(Math.PI);
    iMesh.instances[index].updateMatrix(); // Required after transformations
  8. Enable the rotation property in InstancedMesh2

    master

    By default, InstancedEntity handles rotation using quaternion to avoid the computational overhead of synchronizing quaternion and rotation (Euler angles).

    If your workflow requires using Euler angles, you can enable the rotation property by setting the allowsEuler flag to true in the InstancedMesh2 constructor options. Note that enabling this introduces a minor performance trade-off because the library must perform additional synchronization to keep both representations consistent.

    const iMesh = new InstancedMesh2(geo, mat, { createEntities: true, allowsEuler: true });
  9. Add instances to InstancedMesh2

    master

    You can add instances to an InstancedMesh2 without pre-specifying a length; the underlying buffers will automatically expand as needed. To optimize performance by setting a specific initial buffer capacity, provide the capacity option in the constructor parameters.

    When calling addInstances, you provide the number of instances to add and a callback function. The callback receives the instance object (which behaves like an Object3D) and its index, allowing you to initialize its properties (like position or rotation) immediately.

    // Set initial capacity
    const iMesh = new InstancedMesh2(geo, mat, { capacity: 10000 });
    
    // Add instances with initialization logic
    iMesh.addInstances(count, (obj, index) => {
      obj.position.x = index;
      obj.quaternion.random();
    });
  10. Enable and use InstancedEntity for individual instance control

    master

    By default, InstancedMesh2 does not create individual entity objects for each instance. To enable a simple API for accessing and modifying instance properties (similar to Three.js Object3D), you must set the createEntities flag to true in the constructor parameters.

    Once enabled, you can access an instance via iMesh.instances[index] and perform transformations like setting position or rotation. Note: You must call .updateMatrix() after performing transformations to apply the changes to the mesh.

    // 1. Enable entity creation in the constructor
    const iMesh = new InstancedMesh2(geo, mat, { createEntities: true });
    
    // 2. Access and transform an instance
    iMesh.instances[index].position.random();
    iMesh.instances[index].rotateX(Math.PI);
    
    // 3. Apply the changes
    iMesh.instances[index].updateMatrix();