@realsee/five
Version:
Realsee Renderer SDK
179 lines (135 loc) • 7.37 kB
Markdown
# Raycast (射线检测)
- **Summary**: Five 提供了增强版的射线检测机制,内置 BVH 加速,并扩展了 Three.js Raycaster 以支持更灵活的过滤和性能优化。
- **Schema**: 参见下方 `Three.js Raycaster Extensions`。
- **Concepts**: BVH, IntersectRaycaster, Raycaster Extensions。
- **Examples**: 提供基础检测与性能优化示例。
## Schema: Three.js Raycaster Extensions
> **Definition**: [Raycaster](../../five/declare.d.ts)
Five 对 Three.js 的 `Raycaster` 进行了扩展,在 `declare module 'three'` 中增加了以下属性:
```typescript
// 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;
}
}
```
## Concepts
### 1. Five 内置 BVH (Bounding Volume Hierarchy)
Five 内置了一套高效的 BVH 实现,用于优化 `Mesh` 和 `Points` 的射线检测。
* **作用**: 在进行射线检测时,不直接遍历所有三角形或点,而是先与构建好的 BVH 树(包围盒层次结构)进行相交测试。只有当射线穿过包围盒时,才进一步检测内部的几何体。
* **优势**: 极大提升了复杂模型(如数十万面的 Mesh 或百万级 Points)的碰撞检测性能。
* **透明性**: 开发者通常无需手动操作 BVH,Five 在加载模型时会自动构建,并在调用 `intersectRaycaster` 时自动应用。
### 2. models.intersectRaycaster / model.intersectRaycaster vs 原生 intersectObject
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 时使用。 |
## Examples
### 基础用法:检测点击位置
最常见的场景是将鼠标点击的屏幕坐标转换为 3D 空间中的点。
```typescript
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` 可以显著提升性能。
```typescript
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`。
```typescript
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);
```
## Debugging
* **验证交点**: 在开发阶段,可以在交点处添加一个临时的红色小球 `THREE.Mesh`,直观地确认检测位置是否正确。
* **检查模型可见性**: `intersectRaycaster` 只会检测 `visible = true` 的模型。如果检测不到,请检查模型的可见性状态。
* **Performance**: 如果射线检测导致掉帧,检查是否频繁调用了全场景检测。尝试使用 `firstHitOnly` 或减小检测频率(如节流)。
## Common Pitfalls
1. **混淆 API**: 错误地使用 `raycaster.intersectObject(five.scene)`。虽然这也能工作,但会失去 Five 的 BVH 优化和多模型聚合能力,导致性能下降或漏检。请始终优先使用 `five.model.intersectRaycaster(raycaster)`。
2. **忽略坐标系**: `raycaster.setFromCamera` 需要 NDC 坐标(-1 到 1),而不是像素坐标。务必进行正确的坐标转换。
3. **Tile3D 模型的特殊性**: 对于大型 Tile3D 场景,射线检测可能涉及动态加载的瓦片。Five 内部已处理这些逻辑,外部调用方式不变,但返回的 `object` 可能是某个具体的 Tile 节点。
## Related
* [Parameter](./parameter.md): 了解模型参数配置。
* [Screen Project](./screen-project.md): 屏幕坐标与空间坐标转换(标签、拾取的前置步骤)。
* [Event](./event.md): 手势事件系统,获取点击坐标的来源。
* [Coordinate System](./coordinate-system.md): 坐标系定义,理解 NDC 与世界坐标。
---
```yaml
tags: [点击, 拾取, 碰撞检测, 物体选择, raycast, bvh, interaction, performance, picking, hit-test, click, floor-detection]
```