UNPKG

@realsee/five

Version:

Realsee Renderer SDK

167 lines (125 loc) • 6.72 kB
# Screen & Space Projection (屏幕与空间坐标转换) - **Summary**: 详解屏幕二维坐标与三维空间坐标/射线之间的相互转换关系,以及如何在 DOM 层与 3D 场景进行联动(如显示标签)。 - **Schema**: `five.project2d` 接口定义。 - **Concepts**: World Space, Screen Space, NDC, Raycasting. - **Examples**: 空间点位转屏幕坐标 (标签功能)、屏幕坐标转空间射线。 ## Schema > **Definition**: [Five](../../five/application/five.d.ts) ```typescript // defined in Five class interface Five { /** * 计算三维坐标对应到屏幕的二维坐标 * @param vector - 三维空间坐标 (World Space) * @param testModel - 是否进行模型遮挡检测 (默认 false)。如果为 true,且该点被模型遮挡,则返回 null。 * @returns 二维屏幕坐标 (相对于 Canvas 左上角) 或 null (如果点在视野外或被遮挡) */ project2d(vector: THREE.Vector3, testModel?: boolean): THREE.Vector2 | null; } ``` ## Concepts ### 1. 坐标系转换流程 在 Five (Three.js) 中,坐标转换通常遵循以下流程: * **World Space (世界坐标)**: 三维场景中的绝对坐标 (x, y, z),单位通常为米。 * **Camera Space (相机坐标)**: 相对于相机的坐标。 * **NDC (Normalized Device Coordinates)**: 归一化设备坐标,x, y, z 范围均为 [-1, 1]。 * **Screen Space (屏幕坐标)**: 对应 Canvas 画布的像素坐标。原点 (0, 0) 在左上角,x 向右增加,y 向下增加。 ### 2. 转换关系 | 方向 | 方法 | 核心逻辑 | 用途 | | :--- | :--- | :--- | :--- | | **3D ➔ 2D** | `five.project2d(vector)` | World ➔ NDC ➔ Screen | 标签 (Tag)、HUD、跟随 3D 物体的 UI | | **2D ➔ 3D** | `Raycaster` | Screen ➔ NDC ➔ Ray | 点击拾取 (Picking)、地面检测、放置物体 | ### 3. five.project2d `five.project2d` 是 Five 提供的便捷方法,它封装了以下逻辑: 1. 将世界坐标投影 (Project) 到相机的 NDC 空间。 2. 判断点是否在视锥体 (Frustum) 内(即是否在视野范围内)。 3. (可选) 发射射线检测该点与相机之间是否有模型遮挡。 4. 将 NDC 坐标转换为相对于 Canvas 左上角的像素坐标 (px)。 ## Examples ### 1. 空间标签 (3D Point to DOM Label) 最典型的应用场景是在 3D 场景中的某个物体上方显示一个 HTML 标签。 ```typescript import { Five, Mode } from "@realsee/five"; import * as THREE from "three"; const five = new Five(); // ... 初始化 five ... // 1. 创建标签 DOM 元素 const label = document.createElement("div"); label.textContent = "Living Room"; label.style.position = "absolute"; label.style.padding = "4px 8px"; label.style.background = "rgba(0, 0, 0, 0.6)"; label.style.color = "white"; label.style.borderRadius = "4px"; label.style.pointerEvents = "none"; // 避免阻挡鼠标事件 label.style.transform = "translate(-50%, -100%)"; // 居中并显示在点上方 label.style.display = "none"; // 默认隐藏 document.body.appendChild(label); // 添加到 container (注意 container 的定位 context) // 2. 定义目标空间坐标 (例如客厅中心) const targetPosition = new THREE.Vector3(1.5, 2.0, -3.0); // 3. 在每一帧渲染时更新标签位置 five.on("render.prepare", () => { // 计算屏幕坐标 // 参数2: false 表示不检测遮挡 (性能更好);true 表示如果被墙挡住则隐藏 const screenPos = five.project2d(targetPosition, true); if (screenPos === null) { // 如果返回 null,说明点在相机背面,或者被遮挡(如果开启了 testModel) label.style.display = "none"; } else { label.style.display = "block"; // 更新 DOM 位置 // 注意:screenPos 是相对于 Canvas 的坐标。 // 如果 Canvas 占满全屏,直接使用即可。 // 如果 Canvas 有偏移,需加上 Canvas 的 offset。 label.style.left = `${screenPos.x}px`; label.style.top = `${screenPos.y}px`; } }); ``` ### 2. 屏幕点击转射线 (Screen to Ray) 将鼠标点击的屏幕坐标转换为 3D 射线,用于检测物体。 > **注意**: 更多射线检测细节请参考 [Raycast](./raycast.md)。 ```typescript import * as THREE from "three"; // 假设 container 是 five 的容器元素 const container = five.getElement()!.parentElement!; container.addEventListener("click", (event) => { const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); // 1. 计算 NDC 坐标 (x, y 范围 [-1, 1]) // event.clientX/Y 是相对于视口的坐标 // getBoundingClientRect 用于获取容器的位置 const rect = container.getBoundingClientRect(); mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; // 注意 Y 轴翻转 // 2. 从相机发射射线 raycaster.setFromCamera(mouse, five.camera); // 3. 进行相交检测 const intersects = five.model.intersectRaycaster(raycaster); if (intersects.length > 0) { console.log("Hit point:", intersects[0].point); } }); ``` ## Common Pitfalls 1. **坐标系原点**: * DOM/CSS 坐标系原点在左上角,Y 轴向下。 * NDC/WebGL 坐标系原点在中心,Y 轴向上。 * `project2d` 返回的是 **DOM 风格** 的坐标 (左上角原点),可以直接赋给 CSS `left`/`top`。 * 但在进行 Raycaster 转换时,需要手动将 DOM 坐标转换为 NDC (Y 轴翻转)。 2. **性能开销**: * `project2d(pos, true)` 开启 `testModel` 会进行射线检测,性能开销较大。如果在 `render.prepare` 中对大量点进行检测,可能会导致掉帧。 * 建议:对于大量标签,默认使用 `false`;或者仅对可视范围内的关键标签进行遮挡检测;或者限制检测频率(如每 10 帧检测一次)。 3. **Canvas 尺寸**: * `project2d` 内部使用 `five.renderer.getSize()` 和 `five.viewport` 计算。确保 Canvas 尺寸与 DOM 显示尺寸一致 (通常 Five 会自动处理,但如果使用了 CSS 缩放需注意)。 4. **Null 返回值**: * 务必检查 `project2d` 的返回值是否为 `null`。点在相机背面、超出视锥体裁剪范围、或被遮挡(开启 testModel 时)都会返回 `null`。 ## Related * [Raycast](./raycast.md): 详细的射线检测文档。 * [Five](./five.md): Five 核心类说明。 * [State](./state.md): 了解相机位姿 (Pose)。 * [Coordinate System](./coordinate-system.md): 坐标系定义,理解世界坐标与屏幕坐标的关系。 --- ```yaml tags: [标签, 坐标转换, 信息面板, coordinate, screen, project, label, ui, tag, HUD, 3D转2D, overlay, tooltip] ```