Raycaster
射线投射器,把一次点击或一条方向线转换成一串命中结果,是三维拾取的入口。
构造参数
| 参数 | 默认 | 说明 |
|---|---|---|
origin | (0, 0, 0) | 射线起点,使用 setFromCamera 时会被相机位置覆盖 |
direction | (0, 0, -1) | 方向向量,应归一化 |
near | 0 | 最近命中距离,小于它的结果被忽略 |
far | Infinity | 最远命中距离 |
属性
| 属性 | 说明 |
|---|---|
ray | 底层的 Ray,含 origin 与 direction |
near / far | 距离过滤范围 |
params.Points.threshold | 默认 1,点云拾取的判定半径(世界单位) |
params.Line.threshold | 默认 1,线段拾取的判定半径 |
layers | 只检测指定图层的对象 |
方法
| 方法 | 说明 |
|---|---|
set(origin, direction) | 直接写入起点与方向,方向需先归一化,否则距离与阈值判定会失真 |
setFromCamera(coords, camera) | 按归一化设备坐标生成射线,透视与正交相机都支持 |
intersectObject(obj, recursive, target) | 检测单个对象,recursive 默认 true,会连同后代一起检测 |
intersectObjects(list, recursive, target) | 检测一组对象,结果按距离升序排列 |
命中结果的字段
| 字段 | 说明 |
|---|---|
object | 被命中的对象 |
distance | 起点到命中点的距离,按世界单位 |
point | 命中点的世界坐标 Vector3 |
face | 命中的三角面,含 normal 与三个顶点索引 |
faceIndex | 三角面在几何体中的序号 |
uv | 命中点在该面上的 UV 坐标 |
instanceId | 命中实例化网格时,返回具体实例编号 |
屏幕点击拾取
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
const targets = []; // 只放需要参与拾取的对象
renderer.domElement.addEventListener('pointerdown', function (event) {
const rect = renderer.domElement.getBoundingClientRect();
pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
const hits = raycaster.intersectObjects(targets, false);
if (!hits.length) return;
console.log(hits[0].object.name, hits[0].point.toArray());
});
recursive 传 false 时只检测列表里的对象本身,不会深入子节点。
拾取对象集中且结构扁平时,保持 false 能省掉大量遍历。
与地面求交
不需要真实网格参与时,直接把射线和数学平面求交,成本更低, 适合地形起伏不明显的高空视角。
const ground = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); // y = 0
const hitPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const ok = raycaster.ray.intersectPlane(ground, hitPoint);
if (ok) marker.position.copy(hitPoint);
性能与常见坑
射线检测是 CPU 端的逐三角形计算,几万个面尚可接受, 几百万面的地形每次移动都检测就会明显卡顿。可行做法是把拾取对象限定在 少量代理几何(包围盒、粗网格),命中后再做精确计算。
点云与线段没有面积,只能靠 params.Points.threshold 与
params.Line.threshold 判断,阈值小于点距时会「点不中」,
阈值过大会一次命中一排点。
另外,拾取依赖相机的 matrixWorld 与对象的世界矩阵。
在 OrbitControls 更新后立即拾取,需要先
camera.updateMatrixWorld(),否则射线方向会落后一帧。
拾取的是几何,不是数据。命中结果只给坐标与对象引用,
要拿回要素 ID、属性表或业务字段,需要自己在 object.userData
或外部映射表里建立关联。
经纬志(2026)。《Raycaster》。https://www.jingweizhi.com/three/api/raycaster.html(访问日期 2026-11-03)。