大话HoloLens与Unity(下)
上一篇中,我们已经讨论了HoloLens对于摄像机的接管,以及三种交互方式,但还没有涉及到HoloLens的技术核心——SLAM技术。这次我们就来讨论这个部分。
SLAM
SLAM(Simultaneous Localization and Mapping)是“实时定位与地图构建”的意思,是机器视觉的核心内容,它要解决两个核心问题:
1. 我在哪里
2.我周围是怎样的
SLAM只是一个概念,并不是指的一套具体技术,事实上,很多团队都在发展自己的SLAM技术。而HoloLens也正是基于多年来在SLAM方面的研究成果来创造的。
今天我们的主题并不是SLAM,而是Unity开发,因此我们就不再展开SLAM的话题了,那将是另一篇文章。今天我们来说HoloLens的SLAM技术在Unity中是如何体现的。
Spatial Mapping
HoloLens拥有两颗深度摄像头,依靠它们捕捉周围环境的深度信息,经过HoloLens独有的HPU(Holographic Processing Unit,全息处理单元)加工处理之后,会生成一套周围环境的3D模型。这套模型跟其他的3D模型一样,可以被用来渲染、碰撞、遮挡等等,可以说有了它们,我们制作的各种全息物体就可以和真实世界交互了,这才是MR。
在Unity中,我们通过SurfaceObserver对象来获取空间信息。大致的使用流程是:
1创建SurfaceObserver对象:
注意,这不是一个组件,也就是说它不需要添加到GameObject上,直接new就可以了。
2设置参数:
创建之后,可以设置它的范围等参数,以决定要获取多大空间的信息。
3定期更新:
定期调用Update方法,传入一个回调函数代理,就可以等待它通过通知我们Surface数据更新了。收到更新的通知之后,可以根据回传的参数,处理新增的Surface、改变的Surface,以及移除的Surface。
4获取数据:
我们可以随时去获取surface最新状态,这时需要用到一个异步接口:
SurfaceObserver.RequestMeshAsync。
这里需要创建一个SurfaceData结构体作为参数传入,让此接口去填写,另外传入一个回调函数,填写完成之后,会用回调函数将填好的SurfaceData对象传回。
获取到数据之后,就拿到了当前最新的环境网格、碰撞器和空间锚,之后想做什么就随你了~~
除了直接使用Unity的相关API之外,你也可以使用HoloToolkit进行开发,这里面对于Spatial Mapping功能有很好的封装,可以通过简单的拖放和配置就能实现上述功能。详细信息可以参考HoloToolkit的文档和教程。
注:需要在Unity工程设置里勾选相应的选项才可以使用Spatial Mapping,如下图。更多详情请参看Unity设置的相关文档。
世界坐标与空间锚
HoloLens对空间的认识是渐进式的,也就是说,随着使用者的移动,HoloLens会不断修正对于空间的认识,包括环境网格,和真实空间所对应的坐标。毕竟,HoloLens只对眼前最新识别的环境最敏感。
然而,程序启动之后,Unity中的世界坐标是不会改变的,原点在程序开始时被放在哪里,就一直在哪里,不会随HoloLens对环境的认识而改变。这会导致一个严重的问题: 我们放在Unity空间里的物体,和真实世界可能是不对应的。
这个差异会随着HoloLens对环境认知的变化而变化,表现出来的效果就是,全息物体看起来并没有很稳定的被固定在真实世界之中——它们只会跟随Unity空间,而Unity空间是不会变化、不会被修正的——物体会显得飘飘忽忽的。
要解决这个问题,HoloLens引入了“空间锚(WorldAnchor)”的概念。
空间锚可以理解为一个看不见的物体,它被放置在空间中的时候,会记录它与周围真实环境的位置关系——通过一系列的图像识别技术。在HoloLens不断修正对环境的认识过程中,也会不断的修正所有空间锚的位置,让所有空间锚尽可能保持与真实环境的位置关系不变,这意味着在感官上看起来,空间锚在真实世界中的位置是稳定的 ,虽然实际上空间锚物体对于Unity空间来说坐标一直在变动。
空间锚能够在真实世界中保持稳定了,以它为基准点的坐标系也就跟着稳定下来了,这就是空间锚的核心作用——提供一个相对于真实世界足够稳定的坐标系。如果我们把所有的全息物体都放在空间锚物体的坐标系之中,那么所有全息物体也就都稳定下来了。问题解决了!
然而,实际上并没有那么简单,因为HoloLens对环境的理解总是一直在变化的,我们只能期待距离当前位置比较近的区域内的空间理解是比较准确的。而空间锚的坐标系依然是一个刚性坐标系,这也就意味着,距离空间锚位置很远的地方,坐标系与真实环境之间的差异又会出现,并随距离而放大。实践来看,距离空间锚3米之内的物体,稳定性是比较好的,再远的话就难以保证了。
因此,对于比较大的场景,比较多的物体,我们需要规划更多的空间锚,让空间锚分布在不同的位置,例如形成一个网格,物体则按照最近原则,放入相应的空间锚所在的坐标系之中,这样最能确保稳定性。如果一个物体移动中跨越了空间锚的有效区域,没关系,只需要简单的把这个物体放入新的空间锚坐标系下就可以了。
说起来简单,做起来如何呢?做起来更简单!实际上,在Unity中,任何物体都可以变成空间锚,只要给这个物体添加一个WorldAnchor脚本,这个物体就变成了空间锚,它的位置和旋转将被HoloLens接管, 你再也无法设置它们了。基于Unity的机制,挂在此物体下面的所有物体,其相对坐标就变成了这个空间锚坐标系下的坐标。而且,这个脚本没有任何参数需要配置。怎么样,很简单吧!
但是空间锚被锁定了位置,如果想移动这个空间锚怎么办呢?也很简单,只需要删除WorldAnchor脚本,这个物体就可以再次移动了。移动之后,你随时可以再次添加WorldAnchor脚本,这个物体会就地成为新的空间锚,使用当前所在的位置。
注:空间锚的相关操作,只有在HoloLens上执行才有意义。
Spatial Sound
取悦完眼睛,我们还要取悦耳朵,这就涉及到了HoloLens的另一个强大的功能:空间音效(Spatial Sound)。
首先,HoloLens在两侧拥有两个小音箱。注意!不是耳机,是音箱!你不用往耳朵里塞任何东西,就能清晰地听到声音。
其次,这两个对称的音箱很强大,能够营造出周围空间的真实感受。实际使用中,使用者可以很真实的感受到音源的方向和强度,随着移动,声音也会发生真实的变化。
在Unity中集成了空间音效的插件,可以在Edit->Project Setting->Audio中打开MS HRTF Spatializer。更具体的操作,请参考Spatial Sound的相关文档,这里就不展开了。
不过,声音如何设计,可并非一个简单的代码工作,需要对于声场有着深刻的理解,如果你不了解声音,可能很难把声音做得真实,即使所有的代码工作都没问题。建议你咨询团队中的音效师,或者……咨询其他团队的音效师,他们会给你更多的建议。
总结
终于来到最后了,经过这三篇文章的讨论,我们已经把HoloLens与Unity之间的方方面面都过了一下,相信你对于为何用Unity、如何用Unity开发HoloLens应用有了大致的认识。之后,找一些例子做起来看吧!你会为你能做到的事情而惊讶的。