Optimize Raycaster intersectObject

I am adding markers on a GLTF model and using Raycaster and intersectObject in a forEach loop to hide or show the makers, Everything works fine but the performance is bad with each marker that I add.

https://webdesign-flash.ro/p/emv/

I am new to this, so how can I optimize it, here is a link to the project, and below is the code snippet.

this.markersAR.forEach((marker, index) =>{
           
            // Get initial maker 3D position.
            const marker3DPosition = marker.marker3dPosition;

            // Get 2D screen position and position markers.
            const markerScreenPosition = marker3DPosition.clone();
            markerScreenPosition.project(this.activeCamera);
         
           // Set the raycaster and check the intersection.
           this.markersRaycaster.setFromCamera(markerScreenPosition, this.activeCamera);
           const intersects = this.markersRaycaster.intersectObject(this.model, true);
         
           if(intersects.length === 0){
               marker.show(true);
           }else{
               const intersectionDistance = intersects[0].distance + 0.0001;
               const markerDistance = marker3DPosition.distanceTo(this.activeCamera.position);
             
               if(intersectionDistance >= markerDistance){
                   marker.show(true);
               }else{
                   marker.hide(true);
               } 
           }
       })

“positionMarkers” is getting called every frame, and each marker is doing a raycast.to check if it is occluded (behind the object).

Since your model is pretty dense (lots of triangles) this is really slow.

There are a few different ways to address this…

  1. Use an invisible low poly version of your model that you do the raycasting against.

  2. Use the CSSRenderer but integrated more deeply with threejs. Instead of having it as an overlay on top of the scene… put the cssrenderer Behind the threejs scene, and then for each marker you have the CSS version, and in the threejs scene, and invisible “cutout” mesh to punch through the canvas revealing the CSS markers behind the canvas.
    This is REALLY tricky to get right, but if you get it, then you don’t need to do any raycasting at all… Markers will just naturally go behind the model.

  3. Instead of raycasting… do something simpler like checking if the marker.position.distanceTo(camera.position) is less than < model.position.distanceTo(camera.position)
    This is a relatively cheap check but won’t be as accurate as the raycast solution.

  4. Instead of using CSS for the markers, use a THREE.Sprite or similar and place that in your 3d scene. Then it will naturally disappear behind the model.
    The downside of this, is that you will then have to implement your whole marker clicking pipeline by raycasting against the list of marker sprites.

  5. Use mesh-bvh to accelerate the raycasting on your complex mesh.

  6. Use some kind of existing framework for this that has prepackaged solutions to this problem. :smiley: There may be something like this in the r3f/drei ecosystem.

1 Like

Thank you for your answer.

  1. I can’t because the client might change the model and I have no control over this, this is a model viewer.

  2. I am still a beginner with threejs, this sounds very difficult I don’t even know where to start.

  3. I tried but it is not accurate…

  4. I thought of this but the issue is that in some positions the markers will be partially visible, depending of the model rotation so again is not an option.

  5. This sounds like an option, I will look into it maybe you can show me an example.

  6. I don’t know how to code in react and I invested too much time in this project to learn now React and go into unknow waters :).

https://gkjohnson.github.io/three-mesh-bvh/example/bundle/raycast.html

Thank you for your help the mesh-bvh solved the issue, I tested it with 150 markers and the framerate is 120fps smooth as a baby’s but :slight_smile:

1 Like

Nice! Yeah mesh-bvh is a fantastic tool for your toolbox. @gkjohnson :smiley:

2 Likes