Unity 中的世界锁定和空间定位点
本文内容
让你的全息影像保持原地不动、随你移动,或者在某些情况下相对于其他全息影像定位自己的位置,是创建混合现实应用程序的重要组成部分。 本文将介绍使用 World Locking Tools 的建议解决方案,还将介绍如何在 Unity 项目中手动设置空间定位点。 在讲解代码之前,让我们先来了解 Unity 如何在其自己的引擎中处理坐标空间和定位点,这一点非常重要。
世界规模坐标系
如今,在编写游戏、数据可视化应用或虚拟现实应用时,典型的方法是建立一个所有其他坐标都能可靠映射回的绝对世界坐标系 。 在这种环境下,存在一个稳定的转换方式来定义世界中任何两个物体之间的关系。 如果不移动这些对象,它们的相对变换将始终保持不变。 在渲染一个事先知道所有几何体的纯虚拟世界时,这种全局坐标系正确度很高。 现在,房间规模 VR 应用通常会建立这种绝对房间规模坐标系,其原点在地板上。
相比之下,HoloLens 等不受限制的混合现实设备通过动态传感器来理解世界,当用户在建筑物的整个楼层行走数米时,会不断调整其对周围环境的了解。 在世界规模的体验中,如果你将所有全息影像放在一个简单的刚性坐标系中,这些全息影像最终会随着时间的推移而漂移,无论是基于世界还是相对于彼此。
例如,头戴显示设备当前可能认为世界上的两个位置相距 4 米,然后再完善这种理解,了解到这些位置实际上相距 3.9 米。 如果这些全息影像最初被放在一个单一的刚性坐标系中,相距 4 米,那么其中一个将始终与现实世界相差 0.1 米。
可以在 Unity 中手动放置空间定位点 ,以在用户移动时保持全息影像在物理世界中的位置。 然而,这牺牲了虚拟世界内部的自洽性。 不同的定位点不断地相互移动,并且也在全局坐标空间中移动。 在这个场景中,像布局这样的简单任务会变得困难。 物理模拟也成为难题。
World Locking Tools (WLT) 可以让你两全其美,当用户四处走动时,它使用分布在虚拟场景中的内部空间定位点来稳定单个刚性坐标系 。 WLT 会分析相机的坐标以及每个帧的空间定位点。 WLT 直接修复头部的坐标,而不是通过更改世界上所有事物的坐标来补偿用户头部坐标的校正。
选择世界锁定方法
如果可能,请使用世界锁定工具 进行全息影像定位。
世界锁定工具 提供了一个稳定的坐标系,可以最大程度地减少虚拟和现实世界标记之间的可见不一致。 换句话说,它使用共享的定位点池对整个场景进行世界锁定,而不是使用该组自己的单独定位点锁定每组对象。
世界锁定工具在内部自动处理所有空间定位点的创建和管理。 不需要与 ARAnchorManager 或 WorldAnchor 交互即可保持全息影像处于世界锁定状态。
对于使用 OpenXR 或 Windows XR 插件的 Unity 2019/2020,需要使用 ARAnchorManager 。
对于较旧的 Unity 版本或 WSA 项目,需要使用 WorldAnchor 。
设置世界锁定
若要开始使用世界锁定工具, 请下载混合现实功能工具 。 若要了解有关基础知识的详细信息,请参阅主要“世界锁定工具”文档页,获取“概述”、“快速入门”和其他有用主题的链接。
自动设置
项目准备就绪时,从“混合现实”>“World Locking Tools”运行“配置场景”实用工具 :
重要
随时都可以重新运行“配置场景”实用工具。 例如,如果 AR 目标已从旧版更改为 XR SDK,则应重新运行。 如果场景已正确配置,则运行该实用工具无效。
可视化工具
在早期开发过程中,添加可视化工具有助于确保 WLT 的设置和正常工作。 可以使用“删除可视化工具”实用工具删除它们,以提高生产性能;如果出于任何原因不再需要它们,也可以将其删除。 有关可视化工具的更多详细信息,请参阅 Tools 文档 。
命名空间: UnityEngine.XR.WSA
类型: WorldAnchor
你将使用的一项关键技术是创建一个空间定位点 ,以将一组全息影像精确锁定在物理世界中的位置(无论用户漫游了多远),然后在以后的会话中再次找到这些全息影像 。
在旧版本的 Unity 中,可以通过将 WorldAnchor Unity 组件添加到 GameObject 来创建空间定位点 。
添加世界定位点
要添加世界定位点,请对 GameObject 调用 AddComponent<WorldAnchor>()
,并在现实世界中使用要定位的变换。
WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();
此游戏对象现在定位到物理世界中的当前位置。 你可能会看到其 Unity 世界坐标随着时间的推移略有调整,以确保物理对齐。 请参阅加载世界定位点 ,在以后的应用会话中再次找到该定位点位置。
删除世界定位点
如果不再需要 GameObject
锁定到物理世界位置,并且不想移动此帧,请对世界定位点组件调用 Destroy
。
Destroy(gameObject.GetComponent<WorldAnchor>());
如果要移动 GameObject
此帧,请改为调用 DestroyImmediate
。
DestroyImmediate(gameObject.GetComponent<WorldAnchor>());
移动已进行世界定位的 GameObject
当世界定位点位于上方时,你无法移动 GameObject
。 如果需要移动 GameObject
此帧,需要:
DestroyImmediate
World Anchor 组件。
移动 GameObject
。
向 GameObject
添加新的世界定位点组件。
DestroyImmediate(gameObject.GetComponent<WorldAnchor>());
gameObject.transform.position = new Vector3(0, 0, 2);
WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();
处理可移植性更改
在某个时间点,世界定位点在物理世界中可能不可用。 然后 Unity 不会更新定位对象的转换。 在应用运行时,也可能发生这种情况。 未能处理可定位性更改会导致对象无法出现在世界上的正确物理位置。
要收到有关可定位性更改的通知,需要:
订阅 OnTrackingChanged
事件。 每当基础空间定位点在可定位或不可定位状态之间发生更改时,将调用 OnTrackingChanged
事件。
anchor.OnTrackingChanged += Anchor_OnTrackingChanged;
处理 事件。
private void Anchor_OnTrackingChanged(WorldAnchor self, bool located)
{
// This simply activates/deactivates this object and all children when tracking changes
self.gameObject.SetActiveRecursively(located);
}
如果定位点立即找到,则当 AddComponent<WorldAnchor>()
返回时,定位点的 isLocated
属性将设置为 true
。 因此,不会触发 OnTrackingChanged
事件。 更简洁的模式是在附加定位点后调用具有初始 IsLocated
状态的 OnTrackingChanged
处理程序。
Anchor_OnTrackingChanged(anchor, anchor.isLocated);
持久世界锁定
空间定位点将全息影像保存在应用程序会话之间的现实空间中。 一旦保存在 HoloLens 的定位点存储中,就可以在不同的会话中找到空间定位点并加载;当没有 Internet 连接时,这是一个理想的后备。
重要
本地定位点存储在设备上,而 Azure 空间定位点存储在云中。 你可在同一项目中使用本地定位点和 Azure 定位点,不会发生冲突。 有关集成 Azure 云服务以存储定位点的详细信息,请参阅 Azure 空间定位点 。
默认情况下,世界锁定工具将在支持局部空间定位点持久性的设备上跨会话恢复 Unity 相对于物理世界的坐标系。 若要让全息影像在退出并重新运行应用程序后出现在物理世界的同一个地方,应用程序只需要将相同姿势恢复到全息影像。
如果应用程序需要精细的控制,则可以在检查器中禁用自动保存 和自动加载 ,并从脚本管理持久性。 有关详细信息,请参阅持久化空间坐标系 。
世界锁定工具仅在 HoloLens 设备上支持本地定位暂留。 对于 Android、iOS 和 HoloLens 设备,与 Azure 空间定位点集成,以支持跨会话和设备持久化和共享坐标空间。 有关将世界锁定工具与 Azure 空间定位点配合使用的详细信息和示例,请参阅世界锁定工具 (WLT)与 Azure 空间定位点 (ASA) 结合使用 。
调用 XRAnchorStore
的 API 可在会话之间保留定位点。 XRAnchorStore
是设备上保存的定位点的表示形式。 可以在 Unity 场景中保留来自 ARAnchors
的定位点,将定位点从存储加载到新的 ARAnchors
,或者从存储中删除定位点。
命名空间
对于 Unity 2020 和 OpenXR :
using Microsoft.MixedReality.ARSubsystems.XRAnchorStore
或 Unity 2019/2020 + Windows XR 插件 :
using UnityEngine.XR.WindowsMR.XRAnchorStore
公共方法
{
// A list of all persisted anchors, which can be loaded.
public IReadOnlyList<string> PersistedAnchorNames { get; }
// Clear all persisted anchors
public void Clear();
// Load a single persisted anchor by name. The ARAnchorManager will create this new anchor and report it in
// the ARAnchorManager.anchorsChanged event. The TrackableId returned here is the same TrackableId the
// ARAnchor will have when it is instantiated.
public TrackableId LoadAnchor(string name);
// Attempts to persist an existing ARAnchor with the given TrackableId to the local store. Returns true if
// the storage is successful, false otherwise.
public bool TryPersistAnchor(TrackableId id, string name);
// Removes a single persisted anchor from the anchor store. This will not affect any ARAnchors in the Unity
// scene, only the anchors in storage.
public void UnpersistAnchor(string name);
}
获取定位点存储参考
要使用 Unity 2020 和 OpenXR 加载 XRAnchorStore,请在 ARAnchorManager 的子系统 XRAnchorSubsystem 上使用扩展方法 :
public static Task<XRAnchorStore> LoadAnchorStoreAsync(this XRAnchorSubsystem anchorSubsystem)
要使用 Unity 2019/2020 和 Windows XR 插件加载 XRAnchorStore,请使用 ARReferencePointManager/ARAnchorManager 的子系统 XRReferencePointSubsystem (Unity 2019) 或 XRAnchorSubsystem (Unity 2020) 上的扩展方法 :
// Unity 2019 + Windows XR Plugin
public static Task<XRAnchorStore> TryGetAnchorStoreAsync(this XRReferencePointSubsystem anchorSubsystem);
// Unity 2020 + Windows XR Plugin
public static Task<XRAnchorStore> TryGetAnchorStoreAsync(this XRAnchorSubsystem anchorSubsystem);
加载定位点存储
若要将定位点存储加载到 Unity 2020 和 OpenXR 中,请按如下所示从 ARAnchorManager 的子系统访问它 :
ARAnchorManager arAnchorManager = GetComponent<ARAnchorManager>();
XRAnchorStore anchorStore = await arAnchorManager.subsystem.LoadAnchorStoreAsync();
或使用 Unity 2019/2020 和 Windows XR 插件 :
// Unity 2019
ARReferencePointManager arReferencePointManager = GetComponent<ARReferencePointManager>();
XRAnchorStore anchorStore = await arReferencePointManager.subsystem.TryGetAnchorStoreAsync();
// Unity 2020
ARAnchorManager arAnchorManager = GetComponent<ARAnchorManager>();
XRAnchorStore anchorStore = await arAnchorManager.subsystem.TryGetAnchorStoreAsync();
若要查看持久化/取消定位点的完整示例,请查看定位点 -> 定位点示例 GameObject AnchorsSample.cs 和 [混合现实 OpenXR 插件示例场景](https://github.com/microsoft/OpenXR-Unity-MixedReality-Samples ):
对于旧版 Unity 或 WSA 项目中的全息影像持久性,请使用 WorldAnchor 。
命名空间: UnityEngine.XR.WSA.Persistence
类: WorldAnchorStore
WorldAnchorStore 会创建全息体验,在这种体验中,全息影像跨应用程序实例保持在特定的现实世界位置 。 用户可以将个人全息影像固定在他们需要的任何位置,并在以后在多个应用会话中在同一位置找到它们。
WorldAnchorStore
允许跨会话保留世界定位点的位置。 若要跨会话保留全息影像,请单独跟踪使用特定世界定位点的 GameObjects
。 可以使用世界定位点创建 GameObject
根,并使用本地位置偏移来定位子全息影像。
从以前的会话加载全息影像:
获取 WorldAnchorStore
。
加载世界定位点应用数据,它提供世界定位点的 ID。
按 ID 加载世界定位点。
保存全息影像用于将来的会话:
获取 WorldAnchorStore
。
保存指定 ID 的世界定位点。
保存与世界定位点相关的应用数据以及 ID。
获取 WorldAnchorStore
保留对 WorldAnchorStore
的引用,以便知道何时准备好执行操作。 由于此调用是异步的,因此一旦应用启动,就可以调用:
WorldAnchorStore.GetAsync(StoreLoaded);
当 WorldAnchorStore
完成加载时,StoreLoaded
是处理程序:
private void StoreLoaded(WorldAnchorStore store)
{
this.store = store;
}
现在,有了对 WorldAnchorStore
的引用,便可以保存和加载特定的世界定位点。
保存世界定位点
若要保存世界定位点,请命名世界定位点,并在之前获取的 WorldAnchorStore
中传递它。 如果尝试将两个定位点保存到同一字符串,store.Save
会返回 false。 在保存新的定位点之前,请先删除以前保存的定位点。
private void SaveGame()
{
// Save data about holograms that this world anchor positions
if (!this.savedRoot) // Only save the root once
{
this.savedRoot = this.store.Save("rootGameObject", anchor);
Assert(this.savedRoot);
}
}
加载世界定位点
要加载世界定位点:
private void LoadGame()
{
// Saved data about holograms that this world anchor positions:
this.savedRoot = this.store.Load("rootGameObject", rootGameObject);
if (!this.savedRoot)
{
// Game root not saved. Re-place objects or start over.
}
}
还可以使用 store.Delete()
删除以前保存的定位点,并使用 store.Clear()
删除以前保存的所有数据。
枚举现有定位点
若要列出存储的定位点,请调用 GetAllIds
。
string[] ids = this.store.GetAllIds();
for (int index = 0; index < ids.Length; index++)
{
Debug.Log(ids[index]);
}
为多台设备持久保存全息影像
可以使用 Azure 空间定位点 从本地世界定位点创建持久云定位点。 你的应用可以在多个 HoloLens、iOS 和 Android 设备上找到云定位点,即使这些设备没有同时在一起也是如此。 由于云定位点是永久性的,因此多个设备可以查看相对于该定位点在一段时间内在同一物理位置呈现的内容。
后续步骤
共享世界锁定坐标空间:
了解空间映射:
返回到 Unity 开发检查点:
另请参阅