three-mesh-bvh

repository·master·Indexed 25 days ago

https://github.com/gkjohnson/three-mesh-bvh

A Bounding Volume Hierarchy (BVH) implementation for three.js designed to accelerate raycasting and spatial queries against meshes, including support for high-polygon models, Points, Lines, and BatchedMesh. Features include asynchronous generation via WebWorkers, serialization, and specialized split strategies (CENTER, AVERAGE, SAH) to optimize performance.

Tokens
10.9K
Snippets
9
Records
77
Agent score
75%

What's inside three-mesh-bvh

  1. Use SkinnedMeshBVH for Animated Meshes

    master

    For THREE.SkinnedMesh objects, use SkinnedMeshBVH. It computes primitive bounds using SkinnedMesh.getVertexPosition, meaning the tree reflects the current posed state of the mesh.

    Important: You must call refit() on the SkinnedMeshBVH instance after updating the skeleton to ensure the BVH bounds remain accurate to the current animation pose.

  2. Integrate three-mesh-bvh with three.js using pre-made functions

    master

    You can extend THREE.BufferGeometry, THREE.Mesh, and THREE.BatchedMesh with BVH capabilities by importing extension functions and attaching them to the prototypes. This allows you to use geometry.computeBoundsTree() and enables accelerated raycasting via mesh.raycast automatically.

    import * as THREE from 'three';
    import {
    	computeBoundsTree, disposeBoundsTree,
    	computeBatchedBoundsTree, disposeBatchedBoundsTree,
    	acceleratedRaycast,
    } from 'three-mesh-bvh';
    
    // Add the extension functions
    THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
    THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;
    THREE.Mesh.prototype.raycast = acceleratedRaycast;
    
    THREE.BatchedMesh.prototype.computeBoundsTree = computeBatchedBoundsTree;
    THREE.BatchedMesh.prototype.disposeBoundsTree = disposeBatchedBoundsTree;
    THREE.BatchedMesh.prototype.raycast = acceleratedRaycast;
    
    // Generate geometry and associated BVH
    const geom = new THREE.TorusKnotGeometry( 10, 3, 400, 100 );
    const mesh = new THREE.Mesh( geom, material );
    geom.computeBoundsTree();
    
    // Or generate BatchedMesh and associated BVHs
    const batchedMesh = new THREE.BatchedMesh( ... );
    const geomId = batchedMesh.addGeometry( geom );
    const instId = batchedMesh.addGeometry( geom );
    
    // Generate bounds tree for sub geometry
    batchedMesh.computeBoundsTree( geomId );
  3. Visualize BVH with BVHHelper

    master

    The BVHHelper is a THREE.Group used to visualize a BVH as wireframe bounding boxes or solid face overlays.

    Usage Steps:

    1. Attach the helper as a sibling of the mesh in the scene graph.
    2. Call .update() whenever the mesh's BVH or world transform changes.
    3. Use .dispose() to clean up materials and geometries.
  4. Optimize raycasting with firstHitOnly

    master

    To improve performance when you only need the closest intersection, set the firstHitOnly property of your THREE.Raycaster to true. This instructs the BVH to use the faster raycastFirst method.

    // Setting "firstHitOnly" to true means the Mesh.raycast function will use
    // the bvh "raycastFirst" function to return a result more quickly.
    const raycaster = new THREE.Raycaster();
    raycaster.firstHitOnly = true;
    raycaster.intersectObjects( [ mesh ] );
  5. Generate BVH asynchronously using WebWorkers

    master

    To avoid blocking the main thread during BVH construction, use the worker classes exported from the three-mesh-bvh/worker subpath.

    • GenerateMeshBVHWorker: Standard asynchronous generation.
    • ParallelMeshBVHWorker: Uses SharedArrayBuffer for parallel generation (requires support for SharedArrayBuffer). If unavailable, it falls back to GenerateMeshBVHWorker. It is recommended to use geometries with position and index attributes that use SharedArrayBuffer to avoid buffer copies.
    import { GenerateMeshBVHWorker } from 'three-mesh-bvh/worker';
    
    // ...
    
    const geometry = new KnotGeometry( 1, 0.5, 40, 10 );
    const worker = new GenerateMeshBVHWorker();
    worker.generate( geometry ).then( bvh => {
    
        geometry.boundsTree = bvh;
    
    } );
  6. Run examples locally

    master

    To run the project's examples on your local machine, use the following steps:

    1. Start the development server using npm start.
    2. Open your browser and navigate to localhost:5173/<demo-name>.html.

    Replace <demo-name> with the filename of the specific HTML file located in the example folder.

    npm start
  7. Use the OrientedBox class for oriented bounding box math

    master

    The OrientedBox class is an oriented version of the three.js Box3 class. It allows for intersection tests and distance calculations against oriented bounding boxes.

    Important: Because the class caches derivative values to accelerate intersection functions, you must set .needsUpdate = true whenever you manually modify the min, max, or matrix properties. Alternatively, use the .set() or .copy() methods, which handle the update flag for you.

  8. Important Gotchas and Limitations

    master

    Keep these constraints in mind when using three-mesh-bvh:

    • Local vs World Space: When querying MeshBVH directly, all shapes/rays must be in the local frame of the BVH. THREE.Raycaster handles world-to-local transformation automatically.
    • Static Geometry: The bounds hierarchy is not dynamic. It cannot be used directly with morph targets or skinning. If vertex positions are modified directly, use the refit function to adjust the tree.
    • Geometry Changes: If the geometry changes, you must generate a new bounds tree or call refit.
    • Interleaved Buffers: InterleavedBufferAttributes are not supported with the geometry index buffer attribute.
    • Geometry Groups: A separate bounds tree root is generated for each THREE.Group. This can impact performance on geometry with many groups. Triangles outside these groups are excluded from the BVH.
    • Precision: For very large or off-center geometries, it is recommended to call BufferGeometry.center() before creating the BVH to ensure bounds tightly contain the geometry and to avoid floating-point precision errors.
  9. Query a MeshBVH directly in local space

    master

    When querying a MeshBVH instance directly (bypassing THREE.Raycaster), you must ensure all shapes and rays are transformed into the local space of the geometry. Results are also returned in local space and must be transformed back to world space if needed.

    import * as THREE from 'three';
    import { MeshBVH, acceleratedRaycast } from 'three-mesh-bvh';
    
    let mesh, geometry;
    const invMat = new THREE.Matrix4();
    
    // instantiate the geometry
    
    // ...
    
    const bvh = new MeshBVH( geometry );
    invMat.copy( mesh.matrixWorld ).invert();
    
    // raycasting
    // ensure the ray is in the local space of the geometry being cast against
    raycaster.ray.applyMatrix4( invMat );
    const hit = bvh.raycastFirst( raycaster.ray );
    
    // results are returned in local spac, as well, so they must be transformed into
    // world space if needed.
    hit.point.applyMatrixWorld( mesh.matrixWorld );
    
    // spherecasting
    // ensure the sphere is in the local space of the geometry being cast against
    sphere.applyMatrix4( invMat );
    const intersects = bvh.intersectsSphere( sphere );
  10. Manually build a MeshBVH

    master

    If you prefer not to use prototype extensions, you can manually instantiate a MeshBVH and assign it to the geometry's boundsTree property. You must also manually assign acceleratedRaycast to THREE.Mesh.prototype.raycast to enable the acceleration.

    import * as THREE from 'three';
    import { MeshBVH, acceleratedRaycast } from 'three-mesh-bvh';
    
    // Add the raycast function. Assumes the BVH is available on
    // the `boundsTree` variable
    THREE.Mesh.prototype.raycast = acceleratedRaycast;
    
    // ...
    
    // Generate the BVH and use the newly generated index
    geom.boundsTree = new MeshBVH( geom );
  11. Serialize and Deserialize MeshBVH

    master

    To prevent main thread stuttering, you can generate a MeshBVH asynchronously in a background Web Worker by serializing the BVH, passing the data to a worker, and then deserializing it.

    • MeshBVH.serialize(bvh, options): Generates a representation of the complete bounds tree and the geometry index buffer. Use cloneBuffers: true to ensure the serialized data is independent of the live BVH.
    • MeshBVH.deserialize(data, geometry, options): Recreates a MeshBVH from serialized data. If setIndex: true (default), it will set geometry.index from the serialized index buffer.
  12. Use BVHComputeData for WebGPU compute shaders

    master

    The BVHComputeData class packs scene objects into GPU-accessible BVH buffers (TLAS + BLAS) for use in WebGPU compute shaders via the Three.js TSL node system.

    Workflow:

    1. Instantiate BVHComputeData with your objects.
    2. Call .update() to populate the storage buffers.
    3. Reference this.storage and this.fns within your compute shader nodes.
    WARNING

    This API is unstable and subject to change. It requires three.js r185 or higher.