Three.js Raycaster Extensions。Definition: Raycaster
Five 对 Three.js 的 Raycaster 进行了扩展,在 declare module 'three' 中增加了以下属性:
// defined in five/declare.d.ts
declare module 'three' {
export interface Raycaster {
/**
* 只捕获第一个焦点 (性能优化)
* - true: 找到第一个交点后立即停止检测,返回结果。
* - false (默认): 检测所有可能的交点。
*/
firstHitOnly?: boolean;
/**
* 自定义交点过滤器
* - 回调函数,返回 boolean。
* - true: 保留该交点。
* - false: 丢弃该交点。
*/
hitFilter?: (intersect: THREE.Intersection) => boolean;
/**
* 是否由近及远排序
* - true (默认): 返回的结果数组按距离摄像机从近到远排序。
* - false: 结果顺序不确定(通常按遍历顺序)。
*/
sortByDistance?: boolean;
/**
* 作用楼层 (Five 特有逻辑)
* - 用于多楼层场景过滤。
*/
floorIndex?: number;
}
export interface Intersection {
/** 命中的具体 Tile 对象 (仅 3DTiles) */
tile?: Tile;
/** 命中的 ViewLayer 对象 */
viewLayer?: ViewLayer;
/** 命中的 Model 对象 */
model?: Model;
}
}
Five 内置了一套高效的 BVH 实现,用于优化 Mesh 和 Points 的射线检测。
intersectRaycaster 时自动应用。Five 提供了 model.intersectRaycaster 方法,与 Three.js 原生的 raycaster.intersectObject 有显著区别:
| 特性 | five.model.intersectRaycaster |
raycaster.intersectObject (Three.js 原生) |
|---|---|---|
| 检测范围 | 聚合检测 five.model.shownModels 中所有可见模型。 |
仅检测指定的单个 Object3D 及其子节点。 |
| 结果排序 | 自动合并所有模型的检测结果,并根据 raycaster.sortByDistance 排序。 |
仅对当前 Object 及其子节点的结果排序。 |
| 扩展支持 | 支持 firstHitOnly, hitFilter, sortByDistance 等扩展属性。 |
不支持 Five 的扩展属性(需手动实现)。 |
| 内部优化 | 自动调用模型内部的优化逻辑(如 BVH, Tile3D 的空间索引)。 | 仅进行标准的场景图遍历检测。 |
| 使用场景 | 推荐。适用于全局点击、地板检测、物体拾取等业务场景。 | 仅在需要检测特定原生 Object3D 时使用。 |
最常见的场景是将鼠标点击的屏幕坐标转换为 3D 空间中的点。
import * as THREE from 'three';
import { Five } from '@realsee/five';
const five = new Five();
// ... 初始化 ...
const raycaster = new THREE.Raycaster();
const vector = new THREE.Vector2();
// 假设 onTap 是点击事件处理函数
function onTap(event: { x: number, y: number }) {
// 1. 将屏幕坐标转换为归一化设备坐标 (NDC)
// 注意:five.getElement() 获取 canvas 容器
const element = five.getElement();
if (!element) return;
const rect = element.getBoundingClientRect();
vector.x = ((event.x - rect.left) / rect.width) * 2 - 1;
vector.y = -((event.y - rect.top) / rect.height) * 2 + 1;
// 2. 设置射线
raycaster.setFromCamera(vector, five.camera);
// 3. 调用 Five 的检测方法
const intersects = five.model.intersectRaycaster(raycaster);
if (intersects.length > 0) {
const firstHit = intersects[0];
console.log('点击到了模型:', firstHit.object);
console.log('交点坐标 (世界坐标):', firstHit.point);
}
}
在进行如“地面检测”或“视线遮挡检测”时,通常只需要知道最近的交点。此时开启 firstHitOnly 可以显著提升性能。
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouseVector, five.camera);
// 开启性能优化:找到最近的一个交点后立即停止
raycaster.firstHitOnly = true;
// 结果数组长度最多为 1
const intersects = five.model.intersectRaycaster(raycaster);
if (intersects.length > 0) {
// 处理最近的交点
}
如果你只想检测特定类型的物体(例如只检测地板,忽略天花板),可以使用 hitFilter。
raycaster.hitFilter = (intersect) => {
// 示例:假设地板的法线是向上的 (0, 1, 0)
// 只保留法线朝上的交点(地板)
if (intersect.face && intersect.face.normal.y > 0.5) {
return true; // 保留
}
return false; // 丢弃
};
const intersects = five.model.intersectRaycaster(raycaster);
THREE.Mesh,直观地确认检测位置是否正确。intersectRaycaster 只会检测 visible = true 的模型。如果检测不到,请检查模型的可见性状态。firstHitOnly 或减小检测频率(如节流)。raycaster.intersectObject(five.scene)。虽然这也能工作,但会失去 Five 的 BVH 优化和多模型聚合能力,导致性能下降或漏检。请始终优先使用 five.model.intersectRaycaster(raycaster)。raycaster.setFromCamera 需要 NDC 坐标(-1 到 1),而不是像素坐标。务必进行正确的坐标转换。object 可能是某个具体的 Tile 节点。tags: [点击, 拾取, 碰撞检测, 物体选择, raycast, bvh, interaction, performance, picking, hit-test, click, floor-detection]