空间锚点(Spatial Anchor)的作用
将虚拟物体锚定(固定)在现实世界中的某个位置。每次回到这个位置附近的时候可以看到虚拟物体仍处于完全相同的位置。
- 不受设备重定位的影响
- 退出应用/重启头显后再次打开应用,锚定过的虚拟物体仍然在原来的位置
重定位
设备将世界坐标原点重置在用户当前的位置,原点的z轴朝向和用户的面朝向(z轴朝向)一致。
- 一般在摘下头显熄屏后重新戴上/重新划定安全区/长按Quest右手柄的Home键/用Quest系统手势来重置中心的时候会进行重定位
将OVRManager中的Tracking Origin Type 改为Stage一般不受设备重定位的影响, 但是重新划定安全区或者重启头显打开应用后,世界坐标原点会重置在用户当前的位置。
对比三种情况,如果设置了空间锚点,那么虚拟物体的绝对位置是不会改变的,其余两种情况都会发生变化
场景锚点(Scene Anchor)和空间锚点(Spatial Anchor)的区别
锚点的含义都是锚定的点,所以二者的位置也都是固定的
场景锚点
在 Quest 系统进行空间设置的时候被创建出来,由系统进行管理。空间设置完毕后,系统会将现实房间的布局以及房间内物体的信息保存在场景锚点中,保证重建出来的虚拟房间和现实的房间匹配,墻壁、地板等物体的位置固定不变。比如一个房间有4面墙,空间设置的时候会给每面墙添加一个场景锚点,然后将虚拟的墙创建在场景锚点的位置,让虚拟的墙贴合现实的墙,并且保证墙的位置固定不动。
空间锚点
由应用本身创建,让用户进行管理。比如用户打开应用后,在现实中创建一个虚拟物体,然后可以在虚拟物体身上创建空间锚点,保存空间锚点的位置信息,这样拥有空间锚点的物体就会被固定在当前的位置。应用重新打开后再加载之前保存的空间锚点数据,这样之前锚定的虚拟物体就知道自己应该出现在什么位置,并且能够出现在相同的位置。
Quest目前不能在不同应用程序中可以共享的空间锚点,每个应用中的空间锚点是独立存在的,而Vision OS有共享空间,能够实现在一个空间中打开多个应用,并在它们之间共享空间锚点。
如何判断一个物体属于哪一个空间锚点
空间锚点在创建的时候会作一个组件被添加到物体身上,每一个空间锚点会有一个UUID(通用唯一标识码)。每个UUID通常来说是唯一的、不会重复的一串码,相当于物体的身份证号,用来标识这个物体。
空间锚点存储方式
- 本地存储
- 空间锚点被保存在自己的设备里,之后加载空间锚点的时候就会从设备的本地储存中加 载数据。适合在房间中锚定一些空间小组件,比如在门上贴一个虚拟备忘录、在桌子上 摆放一个虚拟装饰品、在墙上固定一个虚拟的显示屏。可以通过空间锚点中提供的存储 API或者Json、Unity的PlayerPrefs等数据存储方式将锚点信息保存在本地。
- 云端存储(用于共享空间锚点Shared Spatial Anchor)
- 共享空间锚点通常用于本地多人共享MR体验。一个用户的空间锚点可以存储在云端,然后房间里的其他用户可以从云端加载这个用户的空间锚点数据,从而实现共享MR体验。 比如多个人在线下打MR桌游,那么虚拟桌游桌的位置信息就必须共享在所有人的头显中,让大家看到的桌子在同一个位置。
空间锚点的使用方式(提供了哪些类型的API)
使用空间锚点需要通过代码实现,也可以使用BuildingBlock或是Meta的官方样例进行快速开发。
- 创建(Create)锚点
- 保存(Save)锚点
- 加载(Load)锚点
- 销毁(Destroy)锚点【只是在当前的场景中移除掉物体身上的空间锚点,这个物体就不具有空间锚点的特性,但是空间锚点的数据仍然存储在本地或云端】
- 抹除(Erase)锚点【能够真正删除存储在本地或云端的锚点数据】
- 分享(Share)锚点【通常用于共享空间锚点】
使用空间锚点的挑战与限制
- 需要有一定的代码基础,因为开发者要用代码去调用空间锚点的API。不过可以参考Building Block中的空间锚点模块和Meta的StarterSample中的空间锚点示例场景来学习。
- 距离空间锚点3米内的效果是最好的。如果超过3米,锚定的物体可能会发生漂移。
空间锚点的代码实现
下面的代码实现基本上都基于OVRSpatialAnchor这个类,OVRSpatialAnchor类在Unity中主要负责增强现实(AR)和混合现实(MR)应用中的空间锚定功能。它允许开发者在真实世界中创建和管理虚拟对象的持久性位置,使这些对象能够在不同的会话中保持相同的位置和方向。
创建锚点
在Unity的游戏物体身上添加OVRSpatialAnchor组件,有了该组件才会有空间锚点的特性。
锚点的创建过程是异步的,因此会通过几帧的运行完成创建。
不能在AddComponent<OVRSpatialAnchor>的下一行就获取锚点的UUID,因为此时锚点还未创建完毕。
异步与同步的区别
同步与异步是两种不同的执行方式,主要体现在程序执行时是否需要等待某个操作完成。同步同步执行意味着程序在执行某个操作时会被阻塞,必须等待该操作完成并返回结果后才能继续执行下一步。在这种模式下,程序的执行顺序是严格按照代码的书写顺序进行的。例如,在一个函数调用完成之前,程序不会执行下一行代码。这种方式适用于需要精确控制执行顺序的场景,但可能导致程序在等待操作完成时的效率低下[1][2][4][6].特点
- 阻塞:程序在等待结果时无法进行其他操作。
- 顺序执行:操作必须按顺序完成,后续操作依赖于前面的结果。
异步异步执行则允许程序在发起某个操作后继续执行其他任务,而不必等待该操作完成。当操作完成时,程序会通过回调函数或其他机制接收到结果并处理后续逻辑。这种方式提高了程序的效率和响应性,特别是在处理耗时的操作(如网络请求或文件读取)时表现更佳[1][2][3][5].特点
- 非阻塞:程序可以在等待操作结果的同时执行其他任务。
- 并发处理:可以同时处理多个任务,提高效率。
选择使用场景选择同步或异步执行方式应根据具体需求:
- 使用同步:当需要确保操作结果在继续执行后续逻辑之前可用时,适合使用同步方式。例如,用户登录时需要等待验证结果。
- 使用异步:当操作耗时且不需要立即结果时,使用异步可以避免程序卡顿。例如,进行文件上传或数据请求时,可以先进行其他操作,待请求完成后再处理结果。
总结来说,同步和异步的主要区别在于是否需要等待操作完成,前者会导致程序阻塞,而后者则允许程序继续执行其他任务,从而提升整体效率。
保存锚点
调用OVRSpatialAnchor类的
SaveAsync()
方法,通常是在类似于下面的协程函数中,当保存的任务完成时才会退出协程。保存过程也是异步的,默认保存在本地也可以手动更改。
OVRSpace.StorageLocation是存储的位置~.Local是本地,~.Cloud是云端。
加载锚点
三个步骤:
- 借助UUID加载未绑定的锚点
- 锚点定位(Localize)
- 将加载的锚点信息绑定到物体身上新添加的 OVRSpatialAnchor 组件上
借助UUID加载未绑定的锚点
一般应用退出后再次运行程序需要重新加载空间锚点。让物体具有空间锚点的特 性需要给它添加OVRSpatialAnchor组件,但是此时的OVRSpatialAnchor不具有前 一次退出应用前锚点的数据。因此需要先加载出之前保存过的空间锚点数据,之后再把数据赋给新的OVRSpatialAnchor组件。
锚点定位(Localize)
通过设备的SLAM定位,决定锚点的定位和朝向
调用OVRSpatialAnchor.UnboundAnchor.LocalizeAsync()方法
将加载的锚点信息绑定到物体身上新添加的 OVRSpatialAnchor 组件上
抹除(Erase)锚点
调用OVRSpatialAnchor.EraseAsync()方法