Sprite occlusion using raycaster - works but not performant

I have a group, which contains a GLTF and a number of sprites. I want to know which sprites are occluded by the model on each frame that the camera moves.

image

I have this working but it’s not very performant. In the screenshot above you can see that 1 is not occluded but 2 and 3 are - so they have their opacity adjusted.

To achieve this I loop through each sprite (only the ones not frustrum culled) and raycast from the camera to the sprite. If the sprite is the first intersect then I class it as non-occluded:

sprites.forEach((sprite) => {

  sprite.getWorldPosition(spriteWorldPosition);
  let vector = spriteWorldPosition.project(camera);

  raycaster.setFromCamera(new THREE.Vector2(vector.x, vector.y), camera);

  let intersects = raycaster.intersectObject(group, true);

  if (intersects.length > 0 && intersects[0].object === sprite) {
      // occluded = false;
  } else {
      // occluded = true;
  }

});

Is there something I can do to make this more performant?

I’ve tried using three-mesh-bvh but I don’t think I’m fully grasping it, as it seems to make no difference:

In the GLTF loader:

mesh.traverse((child) => {
    if (child.isMesh) {
        const geometry = child.geometry;
        const bvh = new MeshBVH(geometry);
        geometry.boundsTree = bvh;
    }
});

And I’ve also set the following:

THREE.Mesh.prototype.raycast = acceleratedRaycast;
const raycaster = new THREE.Raycaster();
raycaster.firstHitOnly = true;

Any help appreciated!

1 Like

If you’re gonna be doing lots of raycasts per frame, I can hiiiighly recommend using three-mesh-bvh on your scene to accelerate the raycasts.
Built in raycasting falls over when your meshes are even moderately dense and just throwing three-mesh-bvh at it makes raycasting basically instantaneous.


import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast } from 'three-mesh-bvh';

// Add the extension functions
THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;
THREE.Mesh.prototype.raycast = acceleratedRaycast;

... and when you load your meshes:

mesh.geometry.computeBoundsTree();

And now you can go crazy(er) with the raycasting.
(bvh imposes some limitations but its so worth it in terms of what it unlocks)

1 Like

I might be being dumb here, but to get three-mesh-bvh working with a glTF would I do this?

model.traverse((child) => {
    if (child.isMesh) {
        child.geometry.computeBoundsTree();
    }
});

As soon as I loop through the sprites and raycast for each one my framerate drops to 10-15 fps. So I think I’m probably missing something obvious.

Would this idea work for you (no raycasting):
https://codepen.io/boytchev/pen/QWJOBrL

The full discussion is here:

3 Likes

nice trick, depthTest … how do people come up with these things!

1 Like