空间映射

空间映射提供了 HoloLens 周围环境中实际表面的详细表示形式,使开发人员能够创建令人信服的混合现实体验。 通过将现实世界与虚拟世界合并,应用程序可以使全息影像看起来非常真实。 通过提供常见的实际行为和交互,应用程序可以更自然地与用户的预期保持一致。


设备支持

功能 HoloLens(第一代) HoloLens 2 沉浸式头戴显示设备
空间映射

为什么空间映射很重要?

利用空间映射,可以将对象放置在真实的图面上。 这有助于在用户世界锚定对象,并充分利用现实世界的深度提示。基于其他全息影像和现实世界对象遮挡全息影像,有助于让用户相信这些全息影像真实存在于空间中。 如果全息影像漂浮在空间中或随用户一起移动,让人感觉不那么真实。 如果可能,请放置物品以保持舒适。

在放置或移动全息影像时可视化表面(使用投影网格)。 这可以帮助用户了解放置全息影像的最佳位置,并显示他们尝试放置全息影像的位置是否未映射。 可以向用户“宣传项目”,如果它们最终的角度太大。

概念概述

Mesh surfaces covering a room
覆盖房间的空间映射网格示例

用于空间映射的两个主要对象类型是“空间表面观察程序”和“空间表面”。

应用程序为空间表面观察程序提供了一个或多个边界卷,用于定义应用程序希望接收空间映射数据的空间区域。 对于其中每个卷,空间映射将为应用程序提供一组空间表面。

这些卷可能是静止的(位于基于现实世界的固定位置),或者可能附加在 HoloLens 上(当 HoloLens 在环境中移动时,它们会移动,但不会旋转)。 每个空间表面描述了少量空间中的实际表面,这些表面表示为附加到世界锁定的空间坐标系统三角形网格。

当 HoloLens 收集有关环境的新数据时,以及环境发生变化时,空间表面将出现、消失和更改。

空间感知设计概念演示

若要了解空间感知设计概念的运行,请查看下面的“设计全息影像 - 空间感知”视频演示。 完成后,请继续详细了解特定主题。

此视频取自“设计全息影像”HoloLens 2 应用。 请在此处下载并畅享完整体验。

空间映射与场景理解世界网格

对于 HoloLens 2,可以查询静态版本的空间映射数据,并使用场景理解 SDK(EnableWorldMesh 设置)。 下面是访问空间映射数据的两种方法之间的差异:

  • 空间映射 API:
    • 有限范围:类似“气泡”,缓存在用户周围、以有限大小提供给应用程序的空间映射数据。
    • 通过 SurfacesChanged 事件为已更改的网格区域提供低延迟更新。
    • 由“每立方米三角形数量”参数控制的可变详细级别。
  • 场景理解 SDK:
    • 无限范围 - 提供查询半径内所有扫描的空间映射数据。
    • 提供空间映射数据的静态快照。 获取更新的空间映射数据需要对整个网格运行新查询。
    • 由 RequestedMeshLevelOfDetail 设置控制的一致详细级别。

哪些因素会影响空间映射质量?

此处详细介绍的几个因素可能会影响这些错误的频率和严重性。 但是,应该设计应用程序,以便即使空间映射数据中出现错误,用户也可以实现其目标。

常见使用方案

Illustrations of common Spatial Mapping usage scenarios: Placement, Occlusion, Physics and Navigation

定位

空间映射使应用程序有机会向用户呈现自然和熟悉的交互形式;还有什么比把手机放在桌子上更自然的呢?

通过只允许全息图(更概括地说,是任何选定的空间位置)放置在表面上,提供了从 3D(空间中的点)到 2D(表面上的点)的自然映射。 这减少了用户需要向应用程序提供的信息量,使用户能够更快、更轻松且更精确地进行交互。 这是真的,因为我们不习惯用“距离”来与其他人或计算机进行物理通信。 当我们用手指指向时,我们指定的是方向,而不是距离。

此处的一个重要注意事项是,当应用程序推断沿某方向的距离时(例如,沿用户的凝视方向进行光线投射以查找最近的空间表面),必须生成用户能够可靠预测出的结果。 否则,用户将失去控制感,很快变得令人沮丧。 对此,有用的方法是执行多个而不是仅仅一个光线投射。 聚合结果应更平滑且更可预测,更不易受到瞬间“异常值”结果的影响(如由于光线通过小孔或击中用户不知道的几何形状小块)。 还可以随着时间的推移执行聚合或平滑处理:例如,可以限制全息影像与用户之间距离变化的最大速度。 简单地限制最小和最大距离值也有所帮助,因此移动的全息影像不会突然飞向远处或撞回用户的脸上。

应用程序还可使用表面形状和方向来引导全息影像放置。 全息椅子不应穿透墙壁,即使地板略微不平坦,也应与地板齐平。 此类功能可能依赖于物理碰撞的使用,而不是光线投射,但类似的问题同样适用。 如果要放置的全息影像具有许多突出的小型多边形(例如椅子上的腿)时,将这些多边形的物理表示形式扩展到更宽、更平滑的多边形,以便它们能够在空间表面上滑动,而不会被卡住。

在极端情况下,可以完全简化用户输入,空间表面可用于完全自动放置全息影像。 例如,应用程序可以在墙的某一位置放置全息灯光开关,供用户按下。 有关可预测性的相同注意事项在此处同时适用:如果用户希望控制全息影像的放置,但应用程序并不总是将全息影像放在预期的位置(例如,电灯开关出现在用户无法到达的位置),那么这将是一种令人失望的体验。 实际在某些时候,进行用户在自动放置后进行更改可能比要求用户始终手动放置更糟糕;因为用户会预想自动放置正确无误,因而手动更正就像是负担!

另请注意,应用程序使用空间表面进行放置的能力,在很大程度上取决于应用程序的扫描体验。 如果尚未扫描表面,则不能用于放置。 由应用程序决定向用户表明这一点,以便其帮助扫描新表面或选择新位置。

在放置过程中,向用户提供视觉反馈至关重要。 用户需要知道全息影像基于具有接地效果的最近表面的位置。 他们应了解为什么全息影像的移动受到限制(例如,由于与另一个邻近表面发生碰撞)。 如果他们无法将全息影像放置在当前位置,则视觉反馈应明确说明原因。 例如,如果用户试图将全息沙发卡在墙中,则位于墙后的沙发部分应会以红色闪烁。 或者相反,如果应用程序无法在用户可以看到实际表面的位置找到空间表面,则应用程序应明确说明这一点。 如在此区域明显不具备接地效果,可能会达到此目的。

封闭

空间映射表面的主要用途之一是遮挡全息影像。 这种简单的行为对全息影像的感知真实性具有巨大影响,有助于创建一种真正与用户在同一物理空间中的本能感觉。

遮挡还会为用户提供信息:当全息影像似乎被实际表面遮挡时,这为该全息影像在世界中的空间位置提供了额外的视觉反馈。 相反,遮挡也有助于向用户隐藏信息:遮挡墙壁后面的全息影像可以直观地减少视觉混乱。 若要隐藏或显示全息影像,用户只需移动头部。

遮挡还可用于基于常见物理交互的自然用户界面,并提供预期;如果全息影像被表面遮挡,这是因为该表面为实心,因此用户将期望全息影像将与该表面发生碰撞,而非通过表面。

有时,不需要遮挡全息影像。 如果用户需要与全息影像交互,则需要对其进行查看,即使位于真实表面后面。 在这种情况下,当此类全息影像被遮挡时(例如,通过降低其亮度),应当以不同的方式进行渲染。 这样,用户就可以直观地找到全息影像,但他们仍然知道它就在某物后面。

物理

使用物理模拟是空间映射的另一种方式,可用于强化用户物理空间中全息影像的存在。 当一个全息橡胶球逼真地从桌子上滚下来,在地板上弹跳并消失在沙发下时,可能很难相信它并不存在。

物理模拟还为应用程序提供了使用基于物理的自然和常见交互的机会。 如果像在地板上滑动家具一样,并且具有适当的惯性和摩擦力,那么在地板上移动一件全息家具可能会更容易。

若要生成真实的物理行为,可能需要执行一些网格处理,例如填充孔洞、移除漂浮幻象和消除粗糙表面。

还需要考虑应用程序的扫描体验如何影响其物理模拟。 首先,缺失的表面不会与任何东西碰撞;当橡胶球从走廊上滚落下来,离开已知世界的尽头时会发生什么? 其次,随着时间增加,需要决定是否继续响应环境中的变化。 在某些情况下,可能想要尽快做出响应:假设用户使用门和家具作为可移动路障,以抵御刀光箭雨。 但是,在其他情况下,可能想要忽略更新:如果狗狗突然坐在轨道中间,那么在地板上驾驶全息跑车可能会变得不那么有趣。

应用程序可以使用空间映射数据给全息角色(或媒介)授予能力,以真实用户方式导航现实世界。 这可以通过将全息角色限制为同一组自然、常见行为(与用户及其朋友相同)来加强全息角色的存在。

导航功能对用户也很有用。 在给定区域中生成导航地图后,可以共享该地图,为不熟悉该位置的新用户提供全息方向。 此地图旨在帮助保持交通状况平稳,或者避免在危险地点(如建筑工地)发生事故。

实现导航功能所涉及的关键技术挑战将是,可靠地检测出可步行的表面(人类不会在桌子上行走!)以及对环境变化的优雅适应(人类不会穿过关闭的门!)。 网格可能需要进行一些处理,然后才能通过虚拟角色进行路径规划和导航。 对网格进行平滑处理并删除幻象,可有助于避免角色卡住。 可能还希望大幅简化网格,以加快角色的路径规划和导航计算。 在电子游戏技术开发中,这些难题受到很大关注,并且有大量关于这些主题的研究文献。

默认情况下,Unity 中的内置 NavMesh 功能不能用于空间映射表面,因为在应用程序启动之前,这些表面是未知的。 但是,你可以通过安装 NavMeshComponents 在运行期间构建 NavMesh。 另请注意,空间映射系统不会提供有关远离用户当前位置的表面的信息,若要构建大型区域的映射,应用程序必须“记住”这些表面。 你还可以增大空间感知配置文件中的观察范围设置,从而增大可构建 NavMesh 的区域。

可视化效果

大多数情况下,空间表面不可见;以最大程度地减少视觉混乱,让现实世界本色展现。 但是,有时直接可视化空间映射表面很有用,尽管它们的实际对应物是可见的。

例如,当用户尝试将全息影像放置到表面上时(例如,在墙上放置全息机柜),将阴影投射到表面上来使全息影像“接地”会很有用。 这样,用户就更清楚地了解全息影像和表面之间的确切物理邻近度。 这也是在用户提交更改之前直观地“预览”更改的更普遍做法示例。

通过可视化表面,应用程序可以与用户共享其对环境的理解。 例如,全息棋盘游戏可以可视化其标识为“桌子”的水平表面,以便用户知道应在何处进行交互。

可视化表面是向用户显示隐藏在视图中的附近空间的一种有用方法。 可以提供一种方法,使用户可以从客厅访问其厨房(及其所有包含的全息影像)。

空间映射提供的表面网格可能并不特别“干净”。 正确可视化它们非常重要。 传统的照明计算可能会以眼花缭乱的方式突出显示表面法线中的错误,而投射到表面上的“干净”纹理可能有助于赋予其更简洁的外观。 在渲染表面之前,还可以执行网格处理以改进网格属性。

注意

HoloLens 2 实现新的场景理解运行时,为混合现实开发人员提供了结构化的高级环境表示,旨在简化放置、遮挡、物理和导航的实现。

使用表面观察程序

空间映射的起点是表面观察程序。 程序流如下所示:

  • 创建表面观察程序对象
    • 提供一个或多个空间量,用于定义应用程序希望接收空间映射数据的兴趣区域。 空间量只定义空间区域(如球体或盒子)的形状。
    • 使用空间量与世界锁定的空间坐标系统,识别物理世界中的固定区域。
    • 使用空间量(使用正文锁定的空间坐标系统更新每帧)来标识随用户移动(但不旋转)的空间区域。
    • 这些空间量以后可能会随时更改,因为应用程序或用户的状态会发生变化。
  • 使用轮询或通知检索有关空间表面的信息
    • 随时可以“轮询”表面观察程序的空间表面状态。 相反,可以注册表面观察程序的“表面已更改”事件,该事件将在空间表面发生更改时通知应用程序。
    • 对于动态空间量(如视锥或正文锁定卷),应用程序需要设置感兴趣区域,然后获取当前空间表面集来轮询每帧的更改。
    • 对于静态卷,例如覆盖单个房间的世界锁定多维数据集,应用程序可以注册“表面已更改”事件,以在卷中的空间表面可能发生更改时收到通知。
  • 处理表面更改
    • 迭代所提供的空间表面集。
    • 将空间表面分类为已添加、已更改或已删除。
    • 对于每个添加或更改的空间表面,如果适用,请提交异步请求以接收更新后的网格,以所需详细级别表示表面的当前状态。
  • 处理异步网格请求(以下各节将详细介绍)。

网格缓存

空间表面由密集三角形网格表示。 存储、渲染和处理这些网格可能会消耗大量计算和存储资源。 因此,每个应用程序都应采用适合其需求的网格缓存方案,以最大程度地减少用于网格处理和存储的资源。 此方案应确定要保留及要丢弃的网格,以及何时更新每个空间表面的网格。

其中讨论的许多注意事项将直接告知应用程序应如何处理网格缓存。 应考虑用户如何在环境中移动、需要哪种表面、何时观察不同的表面以及何时应捕获环境中的更改。

解释表面观察程序提供的“表面已更改”事件时,网格缓存的基本逻辑如下所示:

  • 如果应用程序发现以前未见过的空间表面 ID,则应将其视为新的空间表面。
  • 如果应用程序发现具有已知 ID,但具有最近更新时间的空间表面,则应将其视为已更新的空间表面。
  • 如果应用程序未发现具有已知 ID 的空间表面,则应将其视为已删除的空间表面。

然后,由每个应用程序做出以下选择:

  • 对于新的空间表面,应请求网格吗?
    • 通常,应该立即请求新空间表面网格,这可能为用户提供有用的新信息。
    • 但是,应优先考虑用户附近和前方的新空间表面,并应首先请求其网格。
    • 如果不需要新网格,例如,应用程序已永久或暂时“冻结”其环境模型,则不应提出请求。
  • 对于已更新的空间表面,应请求网格吗?
    • 应优先考虑用户附近和前方已更新的空间表面,并首先请求其网格。
    • 新表面的优先级可能高于更新后的表面,尤其是在扫描体验期间。
    • 为了限制处理成本,应用程序可能希望限制处理空间表面更新的速率。
    • 可以推断出,空间表面的更改很小,例如,如果表面边界较小,则更新可能不够重要,无法处理。
    • 可以完全忽略对用户当前感兴趣区域之外的空间表面的更新,但在这种情况下,修改表面观察程序使用的空间边界卷可能更有效。
  • 对于已删除的空间表面,应丢弃网格吗?
    • 通常,对于已删除的空间表面,应立即丢弃网格,以便全息影像遮挡保持正确。
    • 但是,如果应用程序有理由相信空间表面将很快重新出现(基于用户体验的设计),则保留它可能比丢弃其网格并在以后再次重新创建更有效。
    • 如果应用程序正在构建用户环境的大规模模型,则其可能不希望丢弃任何网格。 但是,它仍然需要限制资源使用,可能只需在空间表面消失时将网格后台处理到磁盘。
    • 在空间表面生成期间,一些相对罕见事件可能会导致空间表面被位于相似位置但具有不同 ID 的新空间表面替换。 因此,对于选择不丢弃已删除表面的应用程序,应注意不要最终出现覆盖同一位置的多个高度重叠的空间表面网格。
  • 对于任何其他空间表面,应丢弃网格吗?
    • 即使存在空间表面,如果它对用户体验不再有用,也应将其丢弃。 例如,如果应用程序将门口另一侧的房间“替换”为备用虚拟空间,则该房间中的空间表面不再重要。

下面是使用空间和时态滞后的网格缓存策略示例:

  • 请考虑这样一个应用程序,其希望使用视锥形状的空间量,当用户环顾四周和四处走动时,它会跟随用户的注视。
  • 空间表面可能会暂时从此卷中消失,仅仅因为用户将目光移开表面或离表面更远的地方,过了一会儿再回头看或靠近…… 在这种情况下,放弃和重新创建此表面的网格意味着大量冗余处理。
  • 为了减少处理的更改数,应用程序使用两个空间表面观察程序,其中一个被包含在另一个内。 较大的卷是球面的,并“懒惰地”跟随用户;它仅在必要时移动,以确保其中心在用户 2.0 米范围内。
  • 新的和更新的空间表面网格始终从较小的内部表面观察程序进行处理,但网格会进行缓存,直到它们从较大的外部表面观察程序中消失。 这样,应用程序可以避免由于本地用户移动而处理许多冗余更改。
  • 由于空间表面也可能因跟踪丢失而暂时消失,因此应用程序还会在跟踪丢失期间推迟丢弃已删除的空间表面。
  • 通常,应用程序应评估更新处理减少与内存使用量增加之间的平衡,以确定其理想的缓存策略。

渲染

空间映射网格倾向于使用三种主要方式进行渲染:

  • 对于表面可视化
    • 直接可视化空间表面通常十分有用。 例如,在用户将全息影像放置在表面上时,将对象中的“阴影”投射到空间表面上,可以向用户提供更好的视觉反馈。
    • 需要注意的一点是,空间网格不同于三维艺术家可能创建的网格类型。 三角形拓扑不会像人为创建的拓扑那样“干净”,并且网格会受到各种错误的影响。
    • 若要创建令人满意的视觉美感,可能需要进行一些网格处理,例如填充孔洞或平滑表面法线。 还可能希望使用着色器将艺术家设计的纹理投影到网格上,而不是直接可视化网格拓扑和法线。
  • 对于实际表面后面的遮挡全息影像
    • 空间表面可以在纯深度通道中渲染,这只会影响深度缓冲区,而不会影响颜色渲染目标。
    • 这会启动深度缓冲区,遮挡随后在空间表面后面渲染的全息影像。 全息影像的精确遮挡增强了其存在于用户物理空间内的实在感。
    • 若要启用仅深度渲染,请更新混合状态,将所有颜色渲染目标的 RenderTargetWriteMask 设置为零。
  • 用于修改被实际表面遮挡的全息影像的外观
    • 通常,渲染的几何图形在被遮挡时处于隐藏状态。 这是通过将深度模具状态中的深度函数设置为“小于或等于”来实现的,这会使几何图形仅在比相机先前所有渲染的几何图形更近的位置可见。
    • 但是,即使某些几何图形被遮挡,也要保持其可见性,并在遮挡时修改其外观,以便向用户提供视觉反馈,这一点可能有用。 例如,这允许应用程序向用户显示对象的位置,同时清楚地表明其在实际表面的后面。
    • 若要实现此目的,请使用不同的着色器再次渲染几何图形,以创建所需的“遮挡”外观。 在第二次渲染几何图形之前,对深度模具状态进行两个更改。 首先,将深度函数设置为“大于或等于”,以便几何图形仅在比相机先前所有渲染的几何图形更远的位置可见。 其次,将 DepthWriteMask 设置为零,以便深度缓冲区不会修改(深度缓冲区应继续表示距离相机最近的几何图形深度)。

性能是渲染空间映射网格时的一个重要问题。 下面是一些特定于渲染空间映射网格的渲染性能技术:

  • 调整三角形密度
    • 从表面观察程序请求空间表面网格时,需要请求可满足需求的最低密度的三角形网格。
    • 在表面上改变三角密度可能有意义,具体取决于表面与用户的距离以及与用户体验的相关性。
    • 减少三角形数量将减少 GPU 上的内存使用量和顶点处理成本,但不会影响像素处理成本。
  • 使用截锥剔除
    • 截锥剔除会跳过无法看见的绘图对象,因为它们在当前显示的截锥之外。 这同时降低了 CPU 和 GPU 的处理成本。
    • 由于在每个网格基础上执行剔除,空间表面可能会很大,因此将每个空间表面网格分割为较小的区块可能会导致更有效的剔除(因为渲染的屏幕外三角形更少)。 不过存在一种平衡:拥有的网格越多,必须进行的绘图调用就越多,这会增加 CPU 支出。 在极端情况下,截锥剔除计算本身甚至可能具有可测量的 CPU 成本。
  • 调整渲染顺序
    • 空间表面往往很大,因为其代表着用户周围的整个环境。 GPU 上的像素处理成本可能很高,尤其是在有多层可见的几何图形(包括空间表面和其他全息影像)的情况下。 在这种情况下,离用户最近的图层将遮挡更远的所有图层,因此渲染这些图层所花费的任何 GPU 时间都是浪费。
    • 若要减少 GPU 上的这一冗余工作,这有助于按从前到后的顺序呈现不透明表面(首先是较近表面,最后是较远表面)。 “不透明”表面是指在深度模具状态中将 DepthWriteMask 设置为一。 当渲染最近的表面时,它们将预留深度缓冲区,以便 GPU 上的像素处理器能够有效地跳过更远的表面。

网格处理

应用程序可能希望对空间表面网格执行各种操作,以满足其需求。 每个空间表面网格提供的索引和顶点数据,与所有现代渲染 API 中用于渲染三角形网格的顶点和索引缓冲区使用的布局相同。 不过,有一个关键事实需要注意的是,空间映射三角形的缠绕顺序为右面上转。 每个三角形都用网格索引缓冲区中的三个顶点索引表示,当从正面查看三角形时,这些索引会按顺时针顺序标识三角形的顶点。 空间表面网格的正面(或外部)与预期的现实世界表面的正面(可见)相对应。

仅当表面观察程序提供的最粗糙的三角形密度仍然不够粗糙时,应用才应进行网格简化。这种工作的计算成本非常高,且已由运行时执行以生成各种提供的详细信息级别。

由于每个表面观察程序可以提供多个未连接的空间表面,因此某些应用程序可能希望剪裁这些空间表面网格,然后将它们压缩在一起。 通常,剪裁步骤是必需的,因为附近的空间表面网格通常略有重叠。

光线投射和碰撞

为了使物理 API(例如 Havok)为应用程序提供空间表面的光线投射和碰撞功能,应用程序必须向物理 API 提供空间表面网格。 用于物理的网格通常具有以下属性:

  • 它们只包含少量三角形。 物理操作比渲染操作的计算量要多得多。
  • 它们“无懈可击”。 实心表面不应具有孔洞;即使孔洞肉眼不可见,也可能会导致问题。
  • 它们将转换为凸包。 凸包很少有多边形,并且没有孔洞,它们的计算处理效率比原始三角形网格要高得多。

在对空间表面进行光线投射时,请记住,这些表面通常是复杂的、杂乱的形状,充满杂乱的小细节,就像办公桌一样! 这意味着,单个光线投射通常不足以提供有关表面形状及其附近空白空间形状的足够信息。 通常,最好在一个较小的区域中执行多个光线投射,并使用聚合结果来更可靠地理解表面。 例如,与仅使用单个光线投射引导全息影像在表面上的放置相比,使用平均 10 个光线投射将产生更平滑、更少“抖动”的结果。

不过,请记住,每个光线投射的计算成本都很高。 根据使用场景,应权衡额外光线投射的计算成本(每帧一次)与网格处理的计算成本,以平滑和删除空间表面上的孔洞(在空间网格更新时执行)。

环境扫描体验

使用空间映射的每个应用程序都应考虑提供“扫描体验”:应用程序引导用户扫描应用程序正常运行所必需表面的过程。

Example of scanning
扫描示例

此扫描体验的性质可能因每个应用程序的需求而有很大差别,但有两个主要原则应该指导其设计。

首先,清除与用户的通信是主要关注点。 用户应始终知道是否满足应用程序的要求。 当它们不能得到满足时,应立即向用户清楚地说明原因,并且迅速引导用户采取适当的措施。

其次,应用程序应尝试在效率和可靠性之间取得平衡。 如果能够可靠地执行此操作,则应用程序应自动分析空间映射数据以节省用户时间。 如果无法可靠地执行此操作,则应用程序应允许用户快速向应用程序提供所需的其他信息。

若要帮助设计正确的扫描体验,请考虑以下哪些可能性适用于应用程序:

  • 无扫描体验

    • 如果没有任何引导式扫描体验,应用程序可以正常工作;它将了解在自然用户移动过程中观察到的表面。
    • 例如,对于允许用户使用全息喷漆在表面上绘图的应用程序,只需要知道当前用户可见的表面。
    • 如果用户已花费了大量时间使用 HoloLens,则可能已扫描过该环境。
    • 请记住,空间映射使用的相机只能看到用户前面的 3.1 米,因此,空间映射不会知道任何更远的表面,除非用户过去曾经从更近的距离观察过。
    • 因此,用户了解已经扫描了哪些表面,应用程序应提供视觉反馈以达到此效果,例如,将虚拟阴影投射到扫描表面,可能会帮助用户在这些表面上放置全息影像。
    • 对于这种情况,应将空间表面观察程序的边界卷应每帧更新至主体锁定的空间坐标系统,使其跟随用户。
  • 查找合适的位置

    • 应用程序可设计为在具有特定要求的位置使用。
    • 例如,应用程序可能需要用户附近区域保持空旷,以便他们能够安全地练习全息功夫。
    • 应用程序应提前向用户传达任何特定要求,并通过清晰的视觉反馈强化要求。
    • 在此示例中,应用程序应可视化所需空白区域的范围,并直观地突出显示此区域内任何不需要的对象。
    • 对于这种情况,空间表面观察程序的边界卷应在所选位置使用世界锁定的空间坐标系统
  • 查找表面的合适配置

    • 应用程序可能需要特定的表面配置,例如两个高大、平整、相对的墙壁,用以创建全息镜厅。
    • 在这种情况下,应用程序需要对空间映射提供的表面进行分析以检测适当的表面,并将用户定向到这些表面。
    • 如果应用程序的表面分析不可靠,用户应拥有回退选项。 例如,如果应用程序错误地将门识别为墙壁,则用户需要一种简单的方法来更正此错误。
  • 扫描部分环境

    • 应用程序可能希望只捕获用户所指示的部分环境。
    • 例如,应用程序会扫描房间的某个部分,以便用户为希望出售的家具发布全息分类广告。
    • 在这种情况下,应用程序应该捕获用户在扫描期间观察到的区域内的空间映射数据。
  • 扫描整个房间

    • 应用程序可能需要扫描当前房间中的所有表面,包括用户后面。
    • 例如,一个游戏可能会让用户扮演格列佛的角色,并受到数百名从四面八方逼近的小人国人民的围攻。
    • 在这种情况下,应用程序需要确定当前房间中已经扫描了多少个表面,并引导用户的注视,以填充明显的空白。
    • 此过程的关键是提供视觉反馈,使用户清楚地知道尚未扫描哪些表面。 例如,应用程序可以使用基于距离的雾来直观突出显示空间映射表面未覆盖的区域。
  • 获取初始的环境快照

    • 在拍摄初始“快照”后,应用程序可能希望忽略环境中的所有更改。
    • 这可避免与环境中初始状态紧密耦合的用户创建数据中断。
    • 在这种情况下,在扫描完成后,应用程序应在其初始状态下生成空间映射数据的副本。
    • 如果全息影像仍未被环境正确遮挡,则应用程序应继续接收空间制图数据更新。
    • 对于空间映射数据的持续更新可视化已发生的任何更改,并向用户阐明环境的先前状态和当前状态之间的差异。
  • 获取用户发起的环境快照

    • 应用程序可能希望只在用户指示时对环境更改做出响应。
    • 例如,用户可以捕捉朋友在不同时刻的姿势来创建朋友的多个 3D“雕像”。
  • 允许用户更改环境

    • 应用程序可用于实时响应用户环境中所做的任何更改。
    • 例如,用户绘制窗帘可能会触发另一侧的全息游戏发生“场景变化”。
  • 指导用户避免空间映射数据中的错误

需要注意的一个额外细节是,空间映射数据的“范围”不是无限的。 虽然空间映射确实构建了一个大容量的永久数据库,但它只会在用户周围有限大小的“气泡”中使数据可供应用程序使用。 如果从长走廊的起点开始,并且离起点走出足够远的距离,则最终起点的空间表面将消失。 表面在可用空间映射数据中消失后,可以通过在应用程序中缓存表面来缓解这种情况。

网格处理

它可能有助于检测表面中的常见错误类型,并根据需要对空间映射数据进行筛选、删除或修改。

请记住,空间映射数据应尽可能忠实于实际表面,因此,应用的任何处理都可能会使表面进一步偏离“真实”。

下面是一些可能有用的不同类型网格处理示例:

  • 孔洞填充

    • 如果由深色材料制成的小型对象无法扫描,则会在周围表面上留下一个孔洞。
    • 孔洞影响遮挡:在被视为不透明的实际表面,“通过”孔洞能看到全息影像。
    • 孔洞影响光线投射:如果使用光线投射来帮助用户与表面交互,则这些光线可能不需要通过孔洞。 一种缓解措施是,覆盖适当大小的区域,使用多个光线投射的捆绑。 这允许筛选“异常值”结果,这样即使一个光线投射通过小孔,聚合结果仍将有效。 但是,这种方法会产生计算成本。
    • 孔洞影响物理碰撞:由物理模拟控制的对象可能会贯穿地面的孔洞并丢失。
    • 可以通过算法填充表面网格中的此类孔洞。 但是需要调整算法,以防填充门窗等“实际孔洞”。 可能很难可靠地区分“实际孔洞”和“虚拟孔洞”,因此需要尝试不同的试验法,如“大小”和“边界形状”。
  • 删除幻象

    • 反射、明亮灯光和移动对象可能会使微小的“幻象”漂浮在半空中,挥之不去。
    • 幻象影响遮挡:幻象可能会被看见,因为其深色形状略过和遮挡其他全息影像。
    • 幻象影响光线投射:如果使用光线投射来帮助用户与表面进行交互,则这些光线可能会碰到幻象,而不是其背后的表面。 与孔洞一样,一种缓解措施是使用许多而非单个光线投射,但这同样需要计算成本。
    • 幻象影响物理碰撞:由物理模拟控制的对象可能会卡在幻象上,并且无法在看似清晰的空间区域中移动。
    • 可以从表面网格筛选此类幻象。 但是,与孔洞一样,需要调整算法,使小型现实对象(如灯具和门把手)不会被删除。
  • 平滑处理

    • 与实际对应物相比,空间映射可能会返回外观非常粗糙或带有“噪点”的表面。
    • 平滑度影响物理碰撞:如果为粗糙地面,则物理模拟的高尔夫球可能不会以直线平滑地滚动。
    • 平滑度影响渲染:如果直接可视化表面,粗糙的表面法线会影响其破坏“干净”的外观。 可以通过在用于渲染表面的着色器中使用适当的照明和纹理来缓解这种情况。
    • 可以在表面网格中消除粗糙度。 但是,这可能会将表面进一步推离相应的实际表面。 对于生成准确的全息影像遮挡,保持密切的对应关系非常重要,并使用户能够通过全息表面实现精确、可预测的交互。
    • 如果只需要修饰的更改,则它可能足以使顶点法线平滑,而不会改变顶点位置。
  • 平面查找

    • 应用程序可能希望在空间映射提供的表面上执行多种形式的分析。
    • 一个简单的示例是“平面查找”:标识表面的边界(主要是平面区域)。
    • 平面区域可用作全息工作表面,其中全息内容可由应用程序自动放置。
    • 平面区域可以限制用户界面,指导用户与最适合其需求的表面进行交互。
    • 平面区域可以像在现实世界中一样使用,作为功能对象(如液晶屏、表格或白板)的全息对应物。
    • 平面区域可以定义游戏区域,形成视频游戏关卡的基础。
    • 平面区域可以帮助虚拟代理导航现实世界,方法是确定真实人员可能会走到的地面区域。

原型制作和调试

有用的工具

  • HoloLens 仿真器可用于开发使用空间映射的应用程序,而无需访问物理 HoloLens。 使用该功能,可以在实际环境中模拟 HoloLens 上的实时会话,其中包含应用程序通常使用的所有数据,包括 HoloLens 运动、空间坐标系统和空间映射网格。 这可用于提供可靠的可重复输入,对于调试问题和评估代码更改非常有用。
  • 若要重现方案,请通过网络从实时 HoloLens 捕获空间映射数据,然后将其保存到磁盘,并在以后的调试会话中重复使用。
  • Windows 设备门户 3D 视图提供了一种方法,可以查看当前通过空间映射系统获得的所有空间表面。 这为应用程序内的空间表面提供了比较基础:例如,可以很容易地判断是否有任何空间表面丢失或显示在错误的位置。

一般原型制作指南

  • 由于空间映射数据中的错误可能会严重影响用户体验,因此建议在各种环境中测试应用程序。
  • 不要始终在同一位置(例如办公桌)上进行测试。 请确保在不同位置、形状、大小和材料的各种表面上进行测试。
  • 同样,尽管综合数据或记录数据对于调试非常有用,但不要过于依赖于相同的几个测试用例。 这会导致不同测试会发现的重要问题将被更晚发现。
  • 让真实用户(最好是新用户)进行测试是一个不错的做法,因为他们可能不会以标准方式使用 HoloLens 或其他应用程序。 事实上,这可能会让你惊讶于人们的行为、知识和认知的差异化程度!

疑难解答

  • 为了正确定向表面网格,每个 GameObject 都需要处于活动状态,然后才能将其发送到 SurfaceObserver 以构造其网格。 否则,网格将显示在空间中,但会以奇怪的角度旋转。
  • 需要将运行与 SurfaceObserver 通信的脚本的 GameObject 设置为源。 否则,创建并发送到 SurfaceObserver 以构造其网格的所有 GameObjects 都具有与父游戏对象的偏移量相等的偏移量。 这会使网格显示在几米之外,使得正在进行的调试操作变得困难。

另请参阅