I've been looking at the current master source code, not a specific version : github commit b3ce68b4 on sept. 2019.
The source code is located in src/core/Raycaster.js, and the doc is online.
A Raycaster instance is constructed given a ray (origin point + direction vector) and a range on this ray (near, far distances), and offers the following API :
intersectObject( object, recursive, optionalTarget ) : ArrayintersectObjects( objectsArray, recursive, optionalTarget ) : Array
The doc is fairly explicit :
recursive— If true, it also checks all descendants. Otherwise it only checks intersection with the object. Default is false.optionalTarget— (optional) target to set the result. Otherwise a new Array is instantiated. If set, you must clear this array prior to each call (i.e., array.length = 0;).Checks all intersection between the ray and the object with or without the descendants. Intersections are returned sorted by distance, closest first. An array of intersections is returned...
[ { distance, point, face, faceIndex, object }, ... ]
distance– distance between the origin of the ray and the intersectionpoint– point of intersection, in world coordinatesface– intersected facefaceIndex– index of the intersected faceobject– the intersected objectuv- U,V coordinates at point of intersectionuv2- Second set of U,V coordinates at point of intersectionRaycaster delegates to the raycast method of the passed object, when evaluating whether the ray intersects the object or not. This allows meshes to respond differently to ray casting than lines and pointclouds.
Note that for meshes, faces must be pointed towards the origin of the ray in order to be detected; intersections of the ray passing through the back of a face will not be detected. To raycast against both faces of an object, you'll want to set the material's side property to THREE.DoubleSide.
- The code is clean, simple, and easy to follow
- The implementation is based on linear loops on every objects/triangles
- This is, thus, very naïve and not performance oriented/optimized
Its fairly common in raytracing (as in image rendering) to find two types of ray queries :
nearestHitwhich returns only one intersectionanyHitwhich returns only a form of boolean, to know if something was hit (used for shadow rays)- those hints are used to improve performance in ray-intersection search
- threejs does not seem to provide those kind of hints
I also would have expected to get triangle barycentric coordinates from the intersections, as they
can be used to interpolate attributes (such as uv, uv2 for exemple).
In Raycaster code,
intersectObjectsis a simple linear loop over theobjectsarray.- all intersections are sorted once (by
distance), before returning the result array - a helper
intersectObjectfree function does the recursive traversal of objects children- skipping the
!visibleonces - looping the
childrenand makingintersectObjectrecursive calls - the actual intersection for an object is delegated to
object.raycastmethod
- skipping the
The raycast method is available for different primitives :
Sprite, by transforming the sprite vertices to face theraycaster.camera, and intersecting the 2 triangles formed by the spriteLOD, by selecting the LOD object from the ray origin andraycasting it- note the LOD is not related to the
raycaster.camera
- note the LOD is not related to the
Line- check the lines bounding sphere first, for early exit
- linear loop for all lines, check for ray intersection against fattened lines
- push every intersection found in result array
Points- much like the lines, but checking every point/vertex
Mesh- more complex, detailed below
For Mesh primitives, the code is more big :
- check the bounding sphere first, for early exit
- the search ray is transformed in object-space
- using the matrix given by
inverse(object.matrixWorld) - allowing to save on performance later on, the object does not have to be transformed using its
matrixWorld
- using the matrix given by
- intersect the object's optional
boundingBoxfor early exit - linearly loop every triangle of the mesh
- evaluate morphing for the triangle if needed
- note that skinning and bones seem not to be considered
- check intersection with the triangle, considering the applied
materialsidedness- the intersection
distanceis carefully computed using the world-space ray, not using object-space ray, to prevent problems with scale transforms.
- the intersection
- compute intersection UVs if appropriate
- note the barycentric coordinates are completely recomputed using the intersection
point, not reused from any intersection calculations
- note the barycentric coordinates are completely recomputed using the intersection
- push intersection in result array
- evaluate morphing for the triangle if needed
The overall intersect behaviour seem simple and complete, but is a very expensive process.
One must be careful about the number of intersections done in a frame, and carefully select the objects to consider for intersection - or risk a big performance hit.
Performance could be improved using acceleration structures, such as Bounding Volume Hierarchies. Of course, in such a large community, some work have been done on github, e.g. mrdoob/three.js#12857 or https://github.com/gkjohnson/three-mesh-bvh
Another, simple, performance helper would be to add hints to raycaster, allowing to get :
- all intersections, the current behaviour
- the nearest intersection, adjusting the ray
farlimit during the search would allow to discard irreleventraycastcalls - any 'one' intersection, stopping the objects traversale as soon as an intersection is found
I guess most uses of intersection are related to picking objects, 'nearest' intersection could perhaps help, without much more complicated code.