Raycasting & Object Picking
Purpose and Scope
This document explains the raycasting system in Three.js, which enables spatial queries by casting rays through the 3D scene to detect intersections with objects. Raycasting is the foundation for mouse picking, collision detection, line-of-sight queries, and other spatial interaction mechanisms. The system consists of the Raycaster class, mathematical ray primitives, and object-specific intersection algorithms that leverage bounding volume optimizations.
For skeletal animation and mesh skinning topics, see 5.2.
System Architecture
The raycasting system follows a delegation pattern where Raycaster orchestrates the intersection testing process, but individual objects implement their own raycast() methods to compute precise intersections based on their geometry type.
Raycasting System Architecture
The architecture separates concerns: Raycaster handles ray setup, scene traversal, and result sorting, while individual object types implement optimized intersection tests. Bounding volumes provide early rejection to avoid expensive per-triangle tests.
Raycaster Class
Construction and Configuration
The Raycaster class is defined in src/core/Raycaster.js13-228 and provides the main API for performing ray-based spatial queries.
Raycaster Class Structure
Parameters Configuration
The params object allows per-object-type intersection configuration:
| Object Type | Parameter | Default | Purpose |
|---|---|---|---|
| Mesh | - | {} | No configurable parameters |
| Line | threshold | 1 | Maximum distance in world units from line to ray |
| Points | threshold | 1 | Maximum distance in world units from point to ray |
| Sprite | - | {} | No configurable parameters |
| LOD | - | {} | No configurable parameters |
The threshold values enable tolerance-based intersection for thin objects like lines and points that would otherwise be difficult to pick precisely.
Ray Initialization Methods
setFromCamera()
Converts 2D screen coordinates to a 3D ray for mouse picking:
Camera-Based Ray Setup
For perspective cameras, the ray originates at the camera position and points toward the unprojected screen coordinate. For orthographic cameras, all rays are parallel, so the origin is on the near plane and the direction is the camera's forward vector.
setFromXRController()
Sets up the ray from a WebXR controller's world-space position and forward direction:
Ray Mathematics
The Ray class (src/math/Ray.js18-472) encapsulates ray-geometry intersection algorithms. A ray is defined by an origin point and a normalized direction vector.
Core Intersection Methods
Ray Intersection API
Sources: src/math/Ray.js18-472
Triangle Intersection Algorithm
The core mesh intersection uses the Möller-Trumbore algorithm implemented in src/math/Ray.js297-380 This algorithm directly computes the barycentric coordinates and distance without explicitly constructing the plane equation.
The algorithm:
- Computes edge vectors
edge1 = b - aandedge2 = c - a - Calculates determinant from cross product to check if ray is parallel to triangle
- Computes barycentric coordinates
uandv - Validates that point is inside triangle (
u >= 0,v >= 0,u + v <= 1) - Computes distance
talong ray - Returns intersection point if valid
Object-Specific Intersection Algorithms
Each renderable object type implements a raycast(raycaster, intersects) method that computes intersections and appends results to the intersects array.
Mesh Raycasting
Mesh intersection (src/objects/Mesh.js226-405) uses a multi-stage optimization strategy:
Mesh Raycasting Pipeline
The two-stage bounding volume test (sphere in world space, then box in local space) provides early rejection before the expensive per-triangle tests. For meshes with multiple materials, the algorithm respects geometry groups to test only relevant triangles against the appropriate material.
Bounding Volume Optimization
The bounding sphere test in world space (src/objects/Mesh.js236-251):
- Computes or retrieves cached bounding sphere from geometry
- Transforms sphere to world space using
matrixWorld - Tests if ray origin is inside sphere or if ray intersects sphere
- Early exits if no intersection
The bounding box test in local space (src/objects/Mesh.js260-264):
- Transforms ray from world to local space
- Tests ray-box intersection using separating axis theorem
- Provides tighter bounds than sphere for elongated meshes
Triangle Intersection Details
The checkGeometryIntersection() helper (src/objects/Mesh.js440-494):
- Vertex Retrieval: Calls
getVertexPosition()which handles morph target blending (src/objects/Mesh.js176-218) - Sidedness Check: Respects
material.side(FrontSide, BackSide, DoubleSide) by testing triangle winding - Barycentric Coordinates: Uses
Triangle.getBarycoord()to compute point position within triangle - Attribute Interpolation: Interpolates UV coordinates and normals using barycentric weights
- Normal Orientation: Flips interpolated normal if ray hits backface
Line Raycasting
Line intersection (src/objects/Line.js97-184) tests distance from ray to line segments:
Line Raycasting Algorithm
Lines use a threshold-based approach since infinitely thin lines would be impossible to pick. The algorithm finds the closest point on each line segment to the ray and tests if it's within the threshold distance.
Points Raycasting
Points intersection (src/objects/Points.js60-160) tests distance from ray to each point:
The algorithm:
- Tests bounding sphere for early rejection
- Transforms ray to local space
- Retrieves threshold from
raycaster.params.Points - For each point in geometry:
- Tests distance from ray to point
- If within threshold, records intersection with
distanceToRayproperty
- Returns all points within threshold, unsorted by default
Sprite Raycasting
Sprite intersection (src/objects/Sprite.js129-202) treats the sprite as two triangles forming a billboard quad:
Sprite Raycasting Algorithm
Sprites require special handling because they're view-dependent billboards. The algorithm transforms the quad vertices based on the camera's view matrix and tests both triangles of the quad.
Intersection Result Structure
The intersectObject() and intersectObjects() methods return an array of intersection objects sorted by distance (closest first). Each intersection object has the following structure:
| Property | Type | Description |
|---|---|---|
| distance | number | Distance from ray origin to intersection point |
| distanceToRay | number? | For Points: distance from point to ray (undefined for others) |
| point | Vector3 | Intersection point in world coordinates |
| face | Object? | Face definition: |
| faceIndex | number? | Index of the intersected face in the geometry |
| object | Object3D | The intersected 3D object |
| uv | Vector2? | UV coordinates at intersection point |
| uv1 | Vector2? | Secondary UV coordinates at intersection point |
| normal | Vector3? | Interpolated normal vector at intersection point |
| instanceId | number? | For InstancedMesh: index of the intersected instance |
| barycoord | Vector3? | Barycentric coordinates of the intersection point |
The properties marked with ? are only present for certain object types or configurations.
Scene Traversal and Layer Filtering
The intersectObject() method (src/core/Raycaster.js194-202) and intersectObjects() method (src/core/Raycaster.js214-226) handle scene graph traversal with optional recursion.
The internal intersect() function (src/core/Raycaster.js236-259) implements the traversal logic:
Scene Traversal with Layer Filtering
The layer system allows selective raycasting. Each object has a Layers instance, and only objects whose layers overlap with raycaster.layers are tested. Objects can prevent traversal of their children by returning false from raycast().
Performance Characteristics
Optimization Strategies
| Stage | Complexity | Optimization |
|---|---|---|
| Bounding Sphere Test | O(1) | Early rejection in world space |
| Bounding Box Test | O(1) | Early rejection in local space |
| Per-Triangle Test | O(n) where n = triangle count | Requires bounding volume pass |
| Material Groups | O(m) where m = group count | Tests only relevant triangles |
| Vertex Morphing | O(k) where k = morph targets | Blends positions on demand |
The hierarchical bounding volume approach reduces the average case to O(log n) for well-distributed scenes, though worst case remains O(n) for dense geometry.
Memory Efficiency
Raycasting reuses temporary objects allocated at module scope to avoid garbage collection pressure:
_inverseMatrix,_ray,_spherein src/objects/Mesh.js12-14_vA,_vB,_vCfor triangle vertices in src/objects/Mesh.js17-19_intersectionPoint,_intersectionPointWorldin src/objects/Mesh.js24-25
This pattern is consistent across all raycastable object types.
Common Usage Patterns
Mouse Picking
// Setup (once)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// On mouse move
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Cast ray from camera
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const firstHit = intersects[0];
console.log('Hit:', firstHit.object.name, 'at', firstHit.point);
}Selective Layer Raycasting
// Setup picking layer
const pickingLayer = 1;
raycaster.layers.set(pickingLayer);
// Enable layer on interactive objects only
interactiveObject.layers.enable(pickingLayer);Threshold-Based Line Picking
// Increase line pick tolerance
raycaster.params.Line.threshold = 5; // 5 world units
const intersects = raycaster.intersectObjects(lineObjects);