@realsee/five
Version:
Realsee Renderer SDK
114 lines (109 loc) • 28.9 kB
HTML
<html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>features/screen-project | 如视 Five SDK</title><meta name="description" content="Documentation for 如视 Five SDK"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../index.html" class="title">如视 Five SDK</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="" aria-current="page">features/screen-project</a></li></ul></div><div class="tsd-panel tsd-typography"><h1 id="screen-space-projection-屏幕与空间坐标转换" class="tsd-anchor-link">Screen & Space Projection (屏幕与空间坐标转换)<a href="#screen-space-projection-屏幕与空间坐标转换" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h1>
<ul>
<li><strong>Summary</strong>: 详解屏幕二维坐标与三维空间坐标/射线之间的相互转换关系,以及如何在 DOM 层与 3D 场景进行联动(如显示标签)。</li>
<li><strong>Schema</strong>: <code>five.project2d</code> 接口定义。</li>
<li><strong>Concepts</strong>: World Space, Screen Space, NDC, Raycasting.</li>
<li><strong>Examples</strong>: 空间点位转屏幕坐标 (标签功能)、屏幕坐标转空间射线。</li>
</ul>
<h2 id="schema" class="tsd-anchor-link">Schema<a href="#schema" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<blockquote>
<p><strong>Definition</strong>: <a href="../../five/application/five.d.ts">Five</a></p>
</blockquote>
<pre><code class="typescript"><span class="hl-8">// defined in Five class</span><br/><span class="hl-3">interface</span><span class="hl-1"> </span><span class="hl-7">Five</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-8">/**</span><br/><span class="hl-8"> * 计算三维坐标对应到屏幕的二维坐标</span><br/><span class="hl-8"> * </span><span class="hl-3">@param</span><span class="hl-8"> </span><span class="hl-6">vector</span><span class="hl-8"> - 三维空间坐标 (World Space)</span><br/><span class="hl-8"> * </span><span class="hl-3">@param</span><span class="hl-8"> </span><span class="hl-6">testModel</span><span class="hl-8"> - 是否进行模型遮挡检测 (默认 false)。如果为 true,且该点被模型遮挡,则返回 null。</span><br/><span class="hl-8"> * </span><span class="hl-3">@returns</span><span class="hl-8"> 二维屏幕坐标 (相对于 Canvas 左上角) 或 null (如果点在视野外或被遮挡)</span><br/><span class="hl-8"> */</span><br/><span class="hl-1"> </span><span class="hl-5">project2d</span><span class="hl-1">(</span><span class="hl-6">vector</span><span class="hl-1">: </span><span class="hl-7">THREE</span><span class="hl-1">.</span><span class="hl-7">Vector3</span><span class="hl-1">, </span><span class="hl-6">testModel</span><span class="hl-1">?: </span><span class="hl-7">boolean</span><span class="hl-1">): </span><span class="hl-7">THREE</span><span class="hl-1">.</span><span class="hl-7">Vector2</span><span class="hl-1"> | </span><span class="hl-7">null</span><span class="hl-1">;</span><br/><span class="hl-1">}</span>
</code><button type="button">Copy</button></pre>
<h2 id="concepts" class="tsd-anchor-link">Concepts<a href="#concepts" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<h3 id="1-坐标系转换流程" class="tsd-anchor-link">1. 坐标系转换流程<a href="#1-坐标系转换流程" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
<p>在 Five (Three.js) 中,坐标转换通常遵循以下流程:</p>
<ul>
<li><strong>World Space (世界坐标)</strong>: 三维场景中的绝对坐标 (x, y, z),单位通常为米。</li>
<li><strong>Camera Space (相机坐标)</strong>: 相对于相机的坐标。</li>
<li><strong>NDC (Normalized Device Coordinates)</strong>: 归一化设备坐标,x, y, z 范围均为 [-1, 1]。</li>
<li><strong>Screen Space (屏幕坐标)</strong>: 对应 Canvas 画布的像素坐标。原点 (0, 0) 在左上角,x 向右增加,y 向下增加。</li>
</ul>
<h3 id="2-转换关系" class="tsd-anchor-link">2. 转换关系<a href="#2-转换关系" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
<table>
<thead>
<tr>
<th style="text-align:left">方向</th>
<th style="text-align:left">方法</th>
<th style="text-align:left">核心逻辑</th>
<th style="text-align:left">用途</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>3D ➔ 2D</strong></td>
<td style="text-align:left"><code>five.project2d(vector)</code></td>
<td style="text-align:left">World ➔ NDC ➔ Screen</td>
<td style="text-align:left">标签 (Tag)、HUD、跟随 3D 物体的 UI</td>
</tr>
<tr>
<td style="text-align:left"><strong>2D ➔ 3D</strong></td>
<td style="text-align:left"><code>Raycaster</code></td>
<td style="text-align:left">Screen ➔ NDC ➔ Ray</td>
<td style="text-align:left">点击拾取 (Picking)、地面检测、放置物体</td>
</tr>
</tbody>
</table>
<h3 id="3-fiveproject2d" class="tsd-anchor-link">3. five.project2d<a href="#3-fiveproject2d" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
<p><code>five.project2d</code> 是 Five 提供的便捷方法,它封装了以下逻辑:</p>
<ol>
<li>将世界坐标投影 (Project) 到相机的 NDC 空间。</li>
<li>判断点是否在视锥体 (Frustum) 内(即是否在视野范围内)。</li>
<li>(可选) 发射射线检测该点与相机之间是否有模型遮挡。</li>
<li>将 NDC 坐标转换为相对于 Canvas 左上角的像素坐标 (px)。</li>
</ol>
<h2 id="examples" class="tsd-anchor-link">Examples<a href="#examples" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<h3 id="1-空间标签-3d-point-to-dom-label" class="tsd-anchor-link">1. 空间标签 (3D Point to DOM Label)<a href="#1-空间标签-3d-point-to-dom-label" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
<p>最典型的应用场景是在 3D 场景中的某个物体上方显示一个 HTML 标签。</p>
<pre><code class="typescript"><span class="hl-10">import</span><span class="hl-1"> { </span><span class="hl-6">Five</span><span class="hl-1">, </span><span class="hl-6">Mode</span><span class="hl-1"> } </span><span class="hl-10">from</span><span class="hl-1"> </span><span class="hl-9">"@realsee/five"</span><span class="hl-1">;</span><br/><span class="hl-10">import</span><span class="hl-1"> </span><span class="hl-3">*</span><span class="hl-1"> </span><span class="hl-10">as</span><span class="hl-1"> </span><span class="hl-6">THREE</span><span class="hl-1"> </span><span class="hl-10">from</span><span class="hl-1"> </span><span class="hl-9">"three"</span><span class="hl-1">;</span><br/><br/><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">five</span><span class="hl-1"> = </span><span class="hl-3">new</span><span class="hl-1"> </span><span class="hl-5">Five</span><span class="hl-1">();</span><br/><span class="hl-8">// ... 初始化 five ...</span><br/><br/><span class="hl-8">// 1. 创建标签 DOM 元素</span><br/><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">label</span><span class="hl-1"> = </span><span class="hl-6">document</span><span class="hl-1">.</span><span class="hl-5">createElement</span><span class="hl-1">(</span><span class="hl-9">"div"</span><span class="hl-1">);</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">textContent</span><span class="hl-1"> = </span><span class="hl-9">"Living Room"</span><span class="hl-1">;</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">position</span><span class="hl-1"> = </span><span class="hl-9">"absolute"</span><span class="hl-1">;</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">padding</span><span class="hl-1"> = </span><span class="hl-9">"4px 8px"</span><span class="hl-1">;</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">background</span><span class="hl-1"> = </span><span class="hl-9">"rgba(0, 0, 0, 0.6)"</span><span class="hl-1">;</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">color</span><span class="hl-1"> = </span><span class="hl-9">"white"</span><span class="hl-1">;</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">borderRadius</span><span class="hl-1"> = </span><span class="hl-9">"4px"</span><span class="hl-1">;</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">pointerEvents</span><span class="hl-1"> = </span><span class="hl-9">"none"</span><span class="hl-1">; </span><span class="hl-8">// 避免阻挡鼠标事件</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">transform</span><span class="hl-1"> = </span><span class="hl-9">"translate(-50%, -100%)"</span><span class="hl-1">; </span><span class="hl-8">// 居中并显示在点上方</span><br/><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">display</span><span class="hl-1"> = </span><span class="hl-9">"none"</span><span class="hl-1">; </span><span class="hl-8">// 默认隐藏</span><br/><span class="hl-6">document</span><span class="hl-1">.</span><span class="hl-6">body</span><span class="hl-1">.</span><span class="hl-5">appendChild</span><span class="hl-1">(</span><span class="hl-6">label</span><span class="hl-1">); </span><span class="hl-8">// 添加到 container (注意 container 的定位 context)</span><br/><br/><span class="hl-8">// 2. 定义目标空间坐标 (例如客厅中心)</span><br/><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">targetPosition</span><span class="hl-1"> = </span><span class="hl-3">new</span><span class="hl-1"> </span><span class="hl-4">THREE</span><span class="hl-1">.</span><span class="hl-5">Vector3</span><span class="hl-1">(</span><span class="hl-11">1.5</span><span class="hl-1">, </span><span class="hl-11">2.0</span><span class="hl-1">, -</span><span class="hl-11">3.0</span><span class="hl-1">);</span><br/><br/><span class="hl-8">// 3. 在每一帧渲染时更新标签位置</span><br/><span class="hl-6">five</span><span class="hl-1">.</span><span class="hl-5">on</span><span class="hl-1">(</span><span class="hl-9">"render.prepare"</span><span class="hl-1">, () </span><span class="hl-3">=></span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-8">// 计算屏幕坐标</span><br/><span class="hl-1"> </span><span class="hl-8">// 参数2: false 表示不检测遮挡 (性能更好);true 表示如果被墙挡住则隐藏</span><br/><span class="hl-1"> </span><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">screenPos</span><span class="hl-1"> = </span><span class="hl-6">five</span><span class="hl-1">.</span><span class="hl-5">project2d</span><span class="hl-1">(</span><span class="hl-6">targetPosition</span><span class="hl-1">, </span><span class="hl-3">true</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-10">if</span><span class="hl-1"> (</span><span class="hl-6">screenPos</span><span class="hl-1"> === </span><span class="hl-3">null</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-8">// 如果返回 null,说明点在相机背面,或者被遮挡(如果开启了 testModel)</span><br/><span class="hl-1"> </span><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">display</span><span class="hl-1"> = </span><span class="hl-9">"none"</span><span class="hl-1">;</span><br/><span class="hl-1"> } </span><span class="hl-10">else</span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">display</span><span class="hl-1"> = </span><span class="hl-9">"block"</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-8">// 更新 DOM 位置</span><br/><span class="hl-1"> </span><span class="hl-8">// 注意:screenPos 是相对于 Canvas 的坐标。</span><br/><span class="hl-1"> </span><span class="hl-8">// 如果 Canvas 占满全屏,直接使用即可。</span><br/><span class="hl-1"> </span><span class="hl-8">// 如果 Canvas 有偏移,需加上 Canvas 的 offset。</span><br/><span class="hl-1"> </span><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">left</span><span class="hl-1"> = </span><span class="hl-9">`</span><span class="hl-3">${</span><span class="hl-6">screenPos</span><span class="hl-12">.</span><span class="hl-6">x</span><span class="hl-3">}</span><span class="hl-9">px`</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-6">label</span><span class="hl-1">.</span><span class="hl-6">style</span><span class="hl-1">.</span><span class="hl-6">top</span><span class="hl-1"> = </span><span class="hl-9">`</span><span class="hl-3">${</span><span class="hl-6">screenPos</span><span class="hl-12">.</span><span class="hl-6">y</span><span class="hl-3">}</span><span class="hl-9">px`</span><span class="hl-1">;</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span>
</code><button type="button">Copy</button></pre>
<h3 id="2-屏幕点击转射线-screen-to-ray" class="tsd-anchor-link">2. 屏幕点击转射线 (Screen to Ray)<a href="#2-屏幕点击转射线-screen-to-ray" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3>
<p>将鼠标点击的屏幕坐标转换为 3D 射线,用于检测物体。</p>
<blockquote>
<p><strong>注意</strong>: 更多射线检测细节请参考 <a href="features_raycast.html">Raycast</a>。</p>
</blockquote>
<pre><code class="typescript"><span class="hl-10">import</span><span class="hl-1"> </span><span class="hl-3">*</span><span class="hl-1"> </span><span class="hl-10">as</span><span class="hl-1"> </span><span class="hl-6">THREE</span><span class="hl-1"> </span><span class="hl-10">from</span><span class="hl-1"> </span><span class="hl-9">"three"</span><span class="hl-1">;</span><br/><br/><span class="hl-8">// 假设 container 是 five 的容器元素</span><br/><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">container</span><span class="hl-1"> = </span><span class="hl-6">five</span><span class="hl-1">.</span><span class="hl-5">getElement</span><span class="hl-1">()!.</span><span class="hl-6">parentElement</span><span class="hl-1">!;</span><br/><br/><span class="hl-6">container</span><span class="hl-1">.</span><span class="hl-5">addEventListener</span><span class="hl-1">(</span><span class="hl-9">"click"</span><span class="hl-1">, (</span><span class="hl-6">event</span><span class="hl-1">) </span><span class="hl-3">=></span><span class="hl-1"> {</span><br/><span class="hl-1"> </span><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">raycaster</span><span class="hl-1"> = </span><span class="hl-3">new</span><span class="hl-1"> </span><span class="hl-4">THREE</span><span class="hl-1">.</span><span class="hl-5">Raycaster</span><span class="hl-1">();</span><br/><span class="hl-1"> </span><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">mouse</span><span class="hl-1"> = </span><span class="hl-3">new</span><span class="hl-1"> </span><span class="hl-4">THREE</span><span class="hl-1">.</span><span class="hl-5">Vector2</span><span class="hl-1">();</span><br/><br/><span class="hl-1"> </span><span class="hl-8">// 1. 计算 NDC 坐标 (x, y 范围 [-1, 1])</span><br/><span class="hl-1"> </span><span class="hl-8">// event.clientX/Y 是相对于视口的坐标</span><br/><span class="hl-1"> </span><span class="hl-8">// getBoundingClientRect 用于获取容器的位置</span><br/><span class="hl-1"> </span><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">rect</span><span class="hl-1"> = </span><span class="hl-6">container</span><span class="hl-1">.</span><span class="hl-5">getBoundingClientRect</span><span class="hl-1">();</span><br/><br/><span class="hl-1"> </span><span class="hl-6">mouse</span><span class="hl-1">.</span><span class="hl-6">x</span><span class="hl-1"> = ((</span><span class="hl-6">event</span><span class="hl-1">.</span><span class="hl-6">clientX</span><span class="hl-1"> - </span><span class="hl-6">rect</span><span class="hl-1">.</span><span class="hl-6">left</span><span class="hl-1">) / </span><span class="hl-6">rect</span><span class="hl-1">.</span><span class="hl-6">width</span><span class="hl-1">) * </span><span class="hl-11">2</span><span class="hl-1"> - </span><span class="hl-11">1</span><span class="hl-1">;</span><br/><span class="hl-1"> </span><span class="hl-6">mouse</span><span class="hl-1">.</span><span class="hl-6">y</span><span class="hl-1"> = -((</span><span class="hl-6">event</span><span class="hl-1">.</span><span class="hl-6">clientY</span><span class="hl-1"> - </span><span class="hl-6">rect</span><span class="hl-1">.</span><span class="hl-6">top</span><span class="hl-1">) / </span><span class="hl-6">rect</span><span class="hl-1">.</span><span class="hl-6">height</span><span class="hl-1">) * </span><span class="hl-11">2</span><span class="hl-1"> + </span><span class="hl-11">1</span><span class="hl-1">; </span><span class="hl-8">// 注意 Y 轴翻转</span><br/><br/><span class="hl-1"> </span><span class="hl-8">// 2. 从相机发射射线</span><br/><span class="hl-1"> </span><span class="hl-6">raycaster</span><span class="hl-1">.</span><span class="hl-5">setFromCamera</span><span class="hl-1">(</span><span class="hl-6">mouse</span><span class="hl-1">, </span><span class="hl-6">five</span><span class="hl-1">.</span><span class="hl-6">camera</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-8">// 3. 进行相交检测</span><br/><span class="hl-1"> </span><span class="hl-3">const</span><span class="hl-1"> </span><span class="hl-4">intersects</span><span class="hl-1"> = </span><span class="hl-6">five</span><span class="hl-1">.</span><span class="hl-6">model</span><span class="hl-1">.</span><span class="hl-5">intersectRaycaster</span><span class="hl-1">(</span><span class="hl-6">raycaster</span><span class="hl-1">);</span><br/><br/><span class="hl-1"> </span><span class="hl-10">if</span><span class="hl-1"> (</span><span class="hl-6">intersects</span><span class="hl-1">.</span><span class="hl-6">length</span><span class="hl-1"> > </span><span class="hl-11">0</span><span class="hl-1">) {</span><br/><span class="hl-1"> </span><span class="hl-6">console</span><span class="hl-1">.</span><span class="hl-5">log</span><span class="hl-1">(</span><span class="hl-9">"Hit point:"</span><span class="hl-1">, </span><span class="hl-6">intersects</span><span class="hl-1">[</span><span class="hl-11">0</span><span class="hl-1">].</span><span class="hl-6">point</span><span class="hl-1">);</span><br/><span class="hl-1"> }</span><br/><span class="hl-1">});</span>
</code><button type="button">Copy</button></pre>
<h2 id="common-pitfalls" class="tsd-anchor-link">Common Pitfalls<a href="#common-pitfalls" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<ol>
<li>
<p><strong>坐标系原点</strong>:</p>
<ul>
<li>DOM/CSS 坐标系原点在左上角,Y 轴向下。</li>
<li>NDC/WebGL 坐标系原点在中心,Y 轴向上。</li>
<li><code>project2d</code> 返回的是 <strong>DOM 风格</strong> 的坐标 (左上角原点),可以直接赋给 CSS <code>left</code>/<code>top</code>。</li>
<li>但在进行 Raycaster 转换时,需要手动将 DOM 坐标转换为 NDC (Y 轴翻转)。</li>
</ul>
</li>
<li>
<p><strong>性能开销</strong>:</p>
<ul>
<li><code>project2d(pos, true)</code> 开启 <code>testModel</code> 会进行射线检测,性能开销较大。如果在 <code>render.prepare</code> 中对大量点进行检测,可能会导致掉帧。</li>
<li>建议:对于大量标签,默认使用 <code>false</code>;或者仅对可视范围内的关键标签进行遮挡检测;或者限制检测频率(如每 10 帧检测一次)。</li>
</ul>
</li>
<li>
<p><strong>Canvas 尺寸</strong>:</p>
<ul>
<li><code>project2d</code> 内部使用 <code>five.renderer.getSize()</code> 和 <code>five.viewport</code> 计算。确保 Canvas 尺寸与 DOM 显示尺寸一致 (通常 Five 会自动处理,但如果使用了 CSS 缩放需注意)。</li>
</ul>
</li>
<li>
<p><strong>Null 返回值</strong>:</p>
<ul>
<li>务必检查 <code>project2d</code> 的返回值是否为 <code>null</code>。点在相机背面、超出视锥体裁剪范围、或被遮挡(开启 testModel 时)都会返回 <code>null</code>。</li>
</ul>
</li>
</ol>
<h2 id="related" class="tsd-anchor-link">Related<a href="#related" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<ul>
<li><a href="features_raycast.html">Raycast</a>: 详细的射线检测文档。</li>
<li><a href="features_five.html">Five</a>: Five 核心类说明。</li>
<li><a href="features_state.html">State</a>: 了解相机位姿 (Pose)。</li>
<li><a href="features_coordinate-system.html">Coordinate System</a>: 坐标系定义,理解世界坐标与屏幕坐标的关系。</li>
</ul>
<hr>
<pre><code class="yaml"><span class="hl-0">tags</span><span class="hl-1">: [</span><span class="hl-2">标签</span><span class="hl-1">, </span><span class="hl-2">坐标转换</span><span class="hl-1">, </span><span class="hl-2">信息面板</span><span class="hl-1">, </span><span class="hl-2">coordinate</span><span class="hl-1">, </span><span class="hl-2">screen</span><span class="hl-1">, </span><span class="hl-2">project</span><span class="hl-1">, </span><span class="hl-2">label</span><span class="hl-1">, </span><span class="hl-2">ui</span><span class="hl-1">, </span><span class="hl-2">tag</span><span class="hl-1">, </span><span class="hl-2">HUD</span><span class="hl-1">, </span><span class="hl-2">3D转2D</span><span class="hl-1">, </span><span class="hl-2">overlay</span><span class="hl-1">, </span><span class="hl-2">tooltip</span><span class="hl-1">]</span>
</code><button type="button">Copy</button></pre>
</div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#screen-space-projection-屏幕与空间坐标转换"><span>Screen & <wbr/>Space <wbr/>Projection (屏幕与空间坐标转换)</span></a><ul><li><a href="#schema"><span>Schema</span></a></li><li><a href="#concepts"><span>Concepts</span></a></li><li><ul><li><a href="#1-坐标系转换流程"><span>1. 坐标系转换流程</span></a></li><li><a href="#2-转换关系"><span>2. 转换关系</span></a></li><li><a href="#3-fiveproject2d"><span>3. five.project2d</span></a></li></ul></li><li><a href="#examples"><span>Examples</span></a></li><li><ul><li><a href="#1-空间标签-3d-point-to-dom-label"><span>1. 空间标签 (3<wbr/>D <wbr/>Point to <wbr/>DOM <wbr/>Label)</span></a></li><li><a href="#2-屏幕点击转射线-screen-to-ray"><span>2. 屏幕点击转射线 (<wbr/>Screen to <wbr/>Ray)</span></a></li></ul></li><li><a href="#common-pitfalls"><span>Common <wbr/>Pitfalls</span></a></li><li><a href="#related"><span>Related</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">如视 Five SDK</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>