处理AR中的比例问题
在Unite Austin 2017中,Unity的开发人员 Jimmy Alamparambil和Tim Mowrer做了一场演讲《在Unity中使用ARKit和ARCore》。这次演讲中,我们已经为大家介绍了《在ARCore和ARKit中实现多人游戏》,除此之外,演讲重点提到了内容缩放及其必要性。并且解释了为何应该使用“摄像机技巧”来实现缩放,而不是自己动手去缩放内容。关于内容的缩放方式,我们展示了两种方式,一是使用单个摄像机,另一种是使用两个或更多的摄像机。本文中将提供实现的具体细节。
《在Unity中使用ARKit和ARCore》演讲视频,流量党慎入!
https://v.qq.com/txp/iframe/player.html?vid=o0501q3n89t&width=500&height=375&auto=0
基本原理
假设你想制作一个名为“暴怒公鸡”的桌面游戏,一只受辐射变异的巨型公鸡蹂躏城市。你可以先从 Asset Store资源商店下载一个城市环境模型。城市的大小在100米x100米以上。然后将公鸡资源导入到城市场景中。一只0.3米高的公鸡可没有什么威胁感,所以你将它在三个轴向上的大小都放大100倍,使公鸡长到30米高,厉害了!
将一个类似这样的第三人称视角游戏转换成AR的难度有多大?当移植到任何新平台时,总会碰到一些常见问题。例如用户界面改变和不同的输入方式。对于AR来说,还有一个你可能预料不到的问题:比例。
将《暴怒公鸡》游戏转换到ARKit/ARCore,首先需要将特定的AR资源包导入到游戏中。对场景稍作改动,就可以查看场景在AR中的样子了。当启动游戏,你可能只会看到一些占据了整个屏幕的巨大像素。四处移动,屏幕上只会充斥着这些大型像素。完全没有城市和公鸡的影子。问题在哪?
比例
Unity编辑器中的距离单位时米。一个100米大小的城市区域将占据100个单位。ARKit和ARCore的原生单位也是米。听起来似乎两者完美匹配。对于某些AR体验来说,你可能希望资源能和真实世界完美匹配。例如:一个跟你等高的人体模特,展示最新的流行服饰,而你可以绕着它走动。对于桌面体验,你通常会希望资源可以缩小到桌子的可玩区域大小。对于这个例子来说,我们的《暴怒公鸡》城市,放入AR之后需要100米的物理空间,就像真实的城市一样。大多数的桌子基本上只有半米大小,所以我们要怎样将城市放到桌子上是一个问题。
作为一个经验丰富的Unity开发者,缩放城市、公鸡和特效匹配桌子的首选解决方案,可能是将所有东西放到一个变换之下,然后缩放变换对象。视觉上城市和公鸡会缩小,但特效不能正确的缩小。你的公鸡可能还是以原来的速度移动。此外重力也没有按比例缩放,导致所有东西都以疯狂的速度下落。你可以花费大量时间手工调整这些区域,以使它们一一匹配,但如果要确保城市能在不同尺寸的桌子上都能正确缩放,那这就是个近乎不可能的任务了。
幸运的是最新的通用AR脚本接口解决了这个问题,不再需要手工单独调整游戏中的比例。我们提供了两套方案,满足不同需求:一个是设计为对整个场景应用一个通用比例,另一个则允许使用不同的比例组合。
通用比例
将来自设备的所有位置信息乘以一个比例常量是最简单的做法。这包括设备自身的位置以及它生产的数据,例如平面或特征点。旋转信息无需进行缩放。
ARCore和ARKit产生的信息以米为单位,且都是相对于跟踪开始的位置。也就是说,(0, 0, 0)指的时应用启动时所在的位置。所有生成的数据,例如平面,也同样是以此位置为参照点。我们将此称为“设备空间”。
Unity使用的也是米,但我们常常需要将那些好几米高的物体缩小到真实世界中的几厘米大小。我们将这个场景所在的原始空间称为“内容空间”。
将位置信息乘以一个比例因子,可以完成设备空间到内容空间的转换。而缩放内容,则可以反过来进行内容空间到设备空间的转换。后者要困难的多,因为Unity中的许多系统会改变它们的行为,或较难高效的进行缩放。
例如物理交互会因缩放而改变。缩小物理物体会产生不稳定、抖动或其它不期望看到的行为改变。其它的系统,例如地形和导航网格,一旦创建,就无法被缩放或移动。因此对它们来说,不可能进行缩放。粒子系统没有一个总体缩放因子,因此开发者需要调整好几个独立的设置,以保持原先的效果外观。相较而言,对摄像机进行缩放要简单的多了。
在Unite的演示程序,我们将Unity摄像机放在了一个名为“AR Root”的游戏对象之下。所有设备生成的游戏对象,例如平面,都被实例化为摄像机游戏对象的兄弟节点。我们随后可以将比例和位置偏移量应用到AR Root游戏对象,从而移动和缩放摄像机与平面。运行时的层级可能会看起来像下面这样:
AR Root
ARCamera
Plane1
Plane2
…
AR Root游戏对象应当包含一个ARController组件,用于管理AR SDK的生命周期。要设置比例,只需简单的设置ARController.scale。
示例
假定我们的比例因子是10。当应用启动时,设备报告的当前位置时(0,0,0),所以,我们的ARCamera也会在(0,0,0)。我们在现实世界中后退一米,ARCamera的localPosition就会是(0,0,-1)。但是,由于AR Root游戏对象的缩放比是(10,10,10),所以摄像机的世界位置将会是(0,0,-10)。这意味着原先的内容将会离摄像机10倍远,它看起来缩小了10倍。但是,它实际上并没有缩小,因此物理交互不会发生变化,导航网格也会按原来设计的方式运作等等。
命中测试
ARKit和ARCore都提供了针对特征点和平面的命中测试API。但是它们对缩放无感(即命中测试总是在设备空间内进行)。我们可以先从内容空间转换到设备空间,进行命中测试,然后将结果再转换回内容空间。不过对我们的需求来说,只需简单的在平面游戏对象上添加一个网格碰撞器,并对它进行射线投射即可。
我们使用的平面预制件包含一个plane基本体以及一个网格碰撞器,它们被设置为一个单位的缩放能产生一个1x1米大小的平面。由于AR Root游戏对象拥有一个缩放因子,因此也会对平面进行缩放。这意味着我们可以正常进行物理射线投射,而无需考虑比例。例如,假定我们希望在用户轻触屏幕时放置一个对象到一个平面上。我们可以使用下面这样的代码:
参见PlaceOnPlane.cs,了解完整示例。注意其中没有针对比例的特殊处理,因为摄像机和平面都已经处于内容空间中。
匹配比例
上文解释了如何应用某个特定的比例因子,但如果你想将某个特定资源或关卡几何模型匹配到一个表面,而这个表面的大小只有在平面明确后才会知道,这时又该如何?假想你有一个复杂的场景,可能包含了整个城市区域,而你想将它放到一个桌面,刚好匹配桌子的大小。你该选择什么比例因子呢?很简单!正确的比例因子是:
scale = levelSize / surfaceSize
levelSize是内容的大小(以米为单位)。比如,这可能是它边界框其中一条边线的长度。surfaceSize是平面的大小(以真实世界中的米为单位)。这可能是维度中较小边的长度,即:
Mathf.Min(plane.width, plane.height)
参见MatchingScalePicker.cs ,了解完整示例。
关卡几何体的定位和定向
前面提到过某些资源在运行时无法移动或旋转。例如Unity中的地形和导航网格就有这个限制。我们知道如何在运行时缩放它们,但如何对它们进行定位和定向呢?如果你想将关卡几何体放置在一个桌面,代表桌子的平面可能会在任意位置和方向。既然我们无法移动内容,我们可以相应的更改AR Root游戏对象的位置和方向。
例如:如果关卡几何体的中心位于p1,而我希望将它放置在p2,那我们应该移动AR Root游戏对象,以使p1=p2。即,不将关卡几何体移动到p2,而是将AR Root游戏对象朝相反方向移动相同距离。由于平面和摄像机之间没有发生相对位移,因此这样看起来似乎只有关卡几何体发生了移动。
对于方向,我们可以采取同样的做法。因为我们无法旋转关卡几何体来匹配平面的方向,所以我们反向旋转AR Root游戏对象。另一种思考方式是,如果我们在空间中旋转关卡几何体,实际上是将AR Root围绕关卡几何体做反方向的旋转。
ARController组件有一些辅助方法,可以完成这些位置和方向的更改:
public Vector3 ARController.pointOfInterest
“point of interest”是内容空间中,我们希望AR Root游戏对象围绕其运动的中心位置。一般就是关卡几何体的中心点。
public Quaternion ARController.rotation
位于pointOfInterest处的内容看起来应该有的旋转状态。实际上AR Root游戏对象向反方向旋转同样数值。
public void AlignWithPointOfInterest(Vector3 position)
移动AR Root游戏对象,以使point of interest看起来已经“到位”。这可能会是针对某个平面做射线投射后的返回结果。
参见DemoGUI.cs组件,了解代码示例。
混合比例
第二个选择的大致描述如下,相关ARKit插件的代码和项目可参见文末的下载资源列表。目前它还不是ARInterface示例项目的一部分,但过些时候就会加入。
对于双摄像机解决方案,我们使用一个摄像机来跟踪现实世界坐标系统中的游戏对象,例如你正打算在其之上显示内容的桌子的大小和位置。我们将之称为“TrackingDataCamera”,并使它随ARKit设备移动和旋转。它还会渲染所有由ARKit生成的游戏对象,比如调试平面和特征点。
第二个摄像机,我们将它称为“ContentCamera”,它有一个父级变换。对该变换实施定位和旋转,可使由此摄像机渲染的内容场景以正确的大小出现在正确位置。这是如何办到的?首先是“内容锚点”,这是一个真实世界坐标的点,即缩放后场景的出现位置。我们计算该点与TrackingDataCamera之间的偏移。将这个偏移乘以你所希望的场景比例的倒数(例如,如果你希望城市场景缩小到原尺寸的1/100,我们就把偏移量乘以100)。然后将ContentCamera的父变换相对于锚点位置平移这个乘积的量。这个ContentCamera的摄像机变换仍然与TrackedDataCamera的定位与旋转保持一致,因此它的方向与设备相同。参见下面粗略的草图,理解父变换的移动方式。
如何使用它?在UnityARContentScalingScene中,将SkyscraperRoot游戏对象替换为包含你内容的场景对象。当你启动场景,它将会寻找一个表面,而当你轻触表示那个表面的调试平面时,它将使用点击的位置作为前面提到的ContentAnchor(内容锚点)。你的内容将会显示在那个位置。如果你知道你的内容所需的比例,在Content Scale Manager组件的Content Scale参数中找到ContentCameraParent游戏对象,并将所需的值赋予它。GUI上的+和-按钮可以帮你确定内容的正确比例。
双摄像机(或多摄像机)方案在多种情况下十分有用:
需要对多块不同的内容分别进行缩放、移动并/或旋转(而不触碰任何一块内容本身)。
需要使ARKit生成的对象保持真实世界比例,而其它对象看起来缩小了。
需要应用特效,例如屏幕抖动等——存在临时的摄像机移动或缩放。
资源下载
ARKit下载
Bitbucket
https://bitbucket.org/Unity-Technologies/unity-arkit-plugin
Asset Store资源商店
https://www.assetstore.unity3d.com/en/#!/content/92515
ARKit插件的代码和项目
https://bitbucket.org/Unity-Technologies/unity-arkit-plugin/branch/alt-scaled-content
ARInterface示例代码
https://github.com/Unity-Technologies/experimental-ARInterface
小结
正如你所见,我们可以在开发时和应用到现实世界中时采用不同的缩放比,以此创建出更真实更美观的AR体验。我们已经讨论了一些方法,让创作者无需太多工作量即可实现这一目标。我们也为你提供了两个现成的解决方案,你可以在自己的应用中使用。请试用我们的解决方案并在Unity官方社区(unitychina.cn)分享你的使用心得!
推荐阅读
Unity官方活动
11月29日,Unity教育峰会武汉站报名火热进行中。
11月29日晚8点,Unity着色器训练营第二期要开营了,快来参加吧!
“黑色星期五”-Asset Store资源商店促销,一年中最优价格尽在此时!
订阅Unity年度最优折扣,更为中国用户提供发票!
Unity Pro专业版6折,折扣码:FS17PCNPRO40
Unity Plus加强版5折,折扣码:FS17PCNPLUS50
请访问下图中的二维码,立即购买!
点击“阅读原文”进入Unity官方中文社区!