UNPKG

@realsee/five

Version:

Realsee Renderer SDK

179 lines (135 loc) • 7.37 kB
# 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] ```