前言

增强现实(Augmented Reality)是一种在视觉上呈现虚拟物体与现实场景结合的技术。Apple 公司在 2017 年 6 月正式推出了 ARKit,iOS 开发者可以在这个平台上使用简单便捷的 API 来开发 AR 应用程序。

本文将结合美团到餐业务场景,介绍一种基于位置服务(LBS)的 AR 应用。使用 AR 的方式展现商家相对用户的位置,这会给用户带来身临其境的沉浸式体验。下面是实现效果:

项目实现

iOS 平台的 AR 应用通常由 ARKit 和渲染引擎两部分构成:

ARKit 是连接真实世界与虚拟世界的桥梁,而渲染引擎是把虚拟世界的内容渲染到屏幕上。本部分会围绕这两个方面展开介绍。

ARKit

ARKit 的 ARSession 负责管理每一帧的信息。ARSession 做了两件事:拍摄图像并获取传感器数据;对数据进行分析处理后逐帧输出。如下图:

设备追踪

设备追踪确保了虚拟物体的位置不受设备移动的影响。在启动 ARSession 时需要传入一个 ARSessionConfiguration 的子类对象,以区别三种追踪模式:

  • ARFaceTrackingConfiguration
  • ARWorldTrackingConfiguration
  • AROrientationTrackingConfiguration

其中 ARFaceTrackingConfiguration 可以识别人脸的位置、方向以及获取拓扑结构。此外,还可以探测到预设的 52 种丰富的面部动作,如眨眼、微笑、皱眉等等。ARFaceTrackingConfiguration 需要调用支持 TrueDepth 的前置摄像头进行追踪,显然不能满足我们的需求,这里就不做过多的介绍。下面只针对使用后置摄像头的另外两种类型进行对比。

ARWorldTrackingConfiguration

ARWorldTrackingConfiguration 提供 6DoF(Six Degree of Freedom)的设备追踪。包括三个姿态角 Yaw(偏航角)、Pitch(俯仰角)和 Roll(翻滚角),以及沿笛卡尔坐标系中 X、Y 和 Z 三轴的偏移量:

不仅如此,ARKit 还使用了 VIO(Visual-Inertial Odometry)来提高设备运动追踪的精度。在使用惯性测量单元(IMU)检测运动轨迹的同时,对运动过程中摄像头拍摄到的图片进行图像处理。将图像中的一些特征点的变化轨迹与传感器的结果进行比对后,输出最终的高精度结果。

从追踪的维度和准确度来看,ARWorldTrackingConfiguration 非常强悍。但如官方文档所言,它也有两个致命的缺点:

  • 受环境光线质量影响
  • 受剧烈运动影响

由于在追踪过程中要通过采集图像来提取特征点,所以图像的质量会影响追踪的结果。在光线较差的环境下(比如夜晚或者强光),拍摄的图像无法提供正确的参考,追踪的质量也会随之下降。

追踪过程中会逐帧比对图像与传感器结果,如果设备在短时间内剧烈的移动,会很大程度上干扰追踪结果。追踪的结果与真实的运动轨迹有偏差,那么用户看到的商家位置就不准确。

AROrientationTrackingConfiguration

AROrientationTrackingConfiguration 只提供对三个姿态角的追踪(3DoF),并且不会开启 VIO。

Because 3DOF tracking creates limited AR experiences, you should generally not use the AROrientationTrackingConfiguration class directly. Instead, use the subclass ARWorldTrackingConfiguration for tracking with six degrees of freedom (6DOF), plane detection, and hit testing. Use 3DOF tracking only as a fallback in situations where 6DOF tracking is temporarily unavailable.

通常来讲,因为 AROrientationTrackingConfiguration 的追踪能力受限,官方文档不推荐直接使用。但是鉴于:

  1. 对三个姿态角的追踪,已经足以正确的展现商家相对用户的位置了。
  2. ARWorldTrackingConfiguration 的高精度追踪,更适合于距离较近的追踪。比如设备相对桌面、地面的位移。但是商家和用户的距离动辄几百米,过于精确的位移追踪意义不大。
  3. ARWorldTrackingConfiguration 需要规范用户的操作、确保环境光线良好。这对用户来说很不友好。

最终我们决定使用 AROrientationTrackingConfiguration。这样的话,即便是在夜晚,甚至遮住摄像头,商家的位置也能够正确的进行展现。而且剧烈晃动带来的影响很小,商家位置虽然会出现短暂的角度偏差,但是在传感器数值稳定下来后就会得到校准。

坐标轴

ARKit 使用笛卡尔坐标系度量真实世界。ARSession 开启时的设备位置即是坐标轴的原点。而 ARSessionConfiguration 的 worldAlignment 属性决定了三个坐标轴的方向,该属性有三个枚举值:

  • ARWorldAlignmentCamera
  • ARWorldAlignmentGravity
  • ARWorldAlignmentGravityAndHeading

三种枚举值对应的坐标轴如下图所示:

对于 ARWorldAlignmentCamera 来说,设备的姿态决定了三个坐标轴的方向。这种坐标设定适用于以设备作为参考系的坐标计算,与真实地理环境无关,比如用 AR 技术丈量真实世界物体的尺寸。

对于 ARWorldAlignmentGravity 来说,Y 轴方向始终与重力方向平行,而其 X、Z 轴方向仍然由设备的姿态确定。这种坐标设定适用于计算拥有重力属性的物体坐标,比如放置一排氢气球,或者执行一段篮球下落的动画。

对于 ARWorldAlignmentGravityAndHeading 来说,X、Y、Z 三轴固定朝向正东、正上、正南。在这种模式下 ARKit 内部会根据设备偏航角的朝向与地磁真北(非地磁北)方向的夹角不断地做出调整,以确保 ARKit 坐标系中 -Z 方向与我们真实世界的正北方向吻合。有了这个前提条件,真实世界位置坐标才能够正确地映射到虚拟世界中。显然,ARWorldAlignmentGravityAndHeading 才是我们需要的。

商家坐标

商家坐标的确定,包含水平坐标和垂直坐标两部分:

水平坐标

商家的水平位置只是一组经纬度值,那么如何将它对应到 ARKit 当中呢?我们通过下图来说明:

借助 CLLocation 中的 distanceFromLocation:location 方法,可以计算出两个经纬度坐标之间的距离,返回值单位是米。我们可以以用户的经度 lng1、商家的纬度 lat2 作一个辅助点(lng1, lat2),然后分别计算出辅助点距离商家的距离 x、辅助点距离用户的距离 z。ARKit 坐标系同样以米为单位,因而可以直接确定商家的水平坐标(x, -z)。

垂直坐标

对商家地址进行中文分词可以提取出商户所在楼层数,再乘以一层楼大概的高度,以此确定商家的垂直坐标 y 值:

卡片渲染

通常我们想展示的信息,都是通过 UIView 及其子类来实现。但是 ARKit 只负责建立真实世界与虚拟世界的桥梁,渲染的部分还是要交给渲染引擎来处理。Apple 给我们提供了三种可选的引擎:

  • Metal
  • SpriteKit
  • SceneKit

强大的 Metal 引擎包含了 MetalKit、Metal 着色器以及标准库等等工具,可以更高效地利用 GPU,适用于高度定制化的渲染要求。不过 Metal 对于当前需求来说,有些大材小用。

SpriteKit 是 2D 渲染引擎,它提供了动画、事件处理、物理碰撞等接口,通常用于制作 2D 游戏。SceneKit 是 3D 渲染引擎,它建立在 OpenGL 之上,支持多通道渲染。除了可以处理 3D 物体的物理碰撞和动画,还可以呈现逼真的纹理和粒子特效。SceneKit 可以用于制作 3D 游戏,或者在 App 中加入 3D 内容。

虽然我们可以用 SpriteKit 把 2D 的卡片放置到 3D 的 AR 世界中,但是考虑到扩展性,方便之后为 AR 页面添加新的功能,这里我们选用 3D 渲染引擎 SceneKit。

我们可以直接通过创建 ARSCNView 来使用 SceneKit。ARSCNView 是 SCNView 的子类,它做了三件事:

  • 将设备摄像头捕捉的每一帧的图像信息作为 3D 场景的背景。
  • 将设备摄像头的位置作为 3D 场景的摄像头(观察点)位置。
  • 将 ARKit 追踪的真实世界坐标轴与 3D 场景坐标轴重合。

卡片信息

SceneKit 中使用 SCNNode 来管理 3D 物体。设置 SCNNode 的 geometry 属性可以改变物体的外观。系统已经给我们提供了例如 SCNBox、SCNPlane、SCNSphere 等等一些常见的形状,其中 SCNPlane 正是我们所需要的卡片形状。借助 UIGraphics 中的一些方法可以将绘制好的 UIView 渲染成一个 UIImage 对象。根据这张图片创建 SCNPlane,以作为 SCNNode 的外观。

卡片大小

ARKit 中的物体都是近大远小。只要固定好 SCNPlane 的宽高,ARKit 会自动根据距离的远近设置 SCNPlane 的大小。这里列出一个在屏幕上具体的像素数与距离的粗略计算公式,为笔者在开发过程中摸索的经验值:

也就是说,假如 SCNPlane 的宽度为 30,距离用户 100 米,那么在屏幕上看到这个 SCNPlane 的宽度大约为 \(530 / 100 \times 30 = 159\) pt。

卡片位置

对于距离用户过近的商家卡片,会出现两个问题:

  • 由于 ARKit 自动将卡片展现得近大远小,身边的卡片会大到遮住了视野
  • 前文提到的 ARSession 使用 AROrientationTrackingConfiguration 追踪模式,由于没有追踪设备的水平位移,当用户走向商家时,并不会发觉商家卡片越来越近

这里我们将距离用户过近的卡片映射到稍远的位置。如下图所示,距离用户的距离小于 d 的卡片,会被映射到 d-k ~ d 的区间内。

假设某商家距离用户的真实距离为 x,映射后的距离为 y,映射关系如下:

这样既解决了距离过近的问题,又可以保持卡片之间的远近关系。用户位置发生位移到达一定阈值后,会触发一次新的网络请求,根据新的用户位置来重新计算商家的位置。这样随着用户的移动,卡片的位置也会持续地更新。

卡片朝向

SceneKit 会在渲染每一帧之前,根据 SCNNode 的约束自动调整卡片的各种行为,比如碰撞、位置、速度、朝向等等。SCNConstraint 的子类中 SCNLookAtConstraint 和 SCNBillboardConstraint 可以约束卡片的朝向。

SCNLookAtConstraint 可以让卡片始终朝向空间中某一个点。这样相邻的卡片会出现交叉现象,用户看到的卡片信息很可能是不完整的。使用 SCNBillboardConstraint 可以解决这个问题,让卡片的朝向始终与摄像头的朝向平行。

下面是创建卡片的示例代码:


// 位置
SCNVector nodePosition = SCNVectorMake(-200, 5, -80);// 外观
SCNPlane *plane = [SCNPlane planeWithWidth:image.size.widthheight:image.size.height];
plane.firstMaterial.diffuse.contents = image;// 约束
SCNBillboardConstraint *constraint = [SCNBillboardConstraint billboardConstraint];
constraint.freeAxes = SCNBillboardAxisY;SCNNode *node = [SCNNode nodeWithGeometry:plane];
node.position = nodePosition;
node.constraints = @[constraint];

优化

遮挡问题

如果同一个方向的商家数量有很多,那么卡片会出现互相重叠的现象,这会导致用户只能看到离自己近的卡片。这是个比较棘手的问题,如果在屏幕上平铺卡片的话,既牺牲了对商家高度的感知,又无法体现商家距离用户的远近关系。

点击散开的交互方式

经过漫长的讨论,我们最终决定采取点击重叠区域后,卡片向四周分散的交互方式来解决重叠问题,效果如下:

下面围绕点击和投射两个部分,介绍该效果的实现原理。

点击

熟悉 Cocoa Touch 的朋友都了解,UIView 的层级结构是通过 hit-testing 来判断哪个视图响应事件的,在 ARKit 中也不例外。

ARSCNView 可以使用两种 hit-testing:

  • 来自 ARSCNView 的 hitTest:types: 方法:查找点击的位置所对应的真实世界中的物体或位置
  • 来自 SCNSceneRenderer 协议的 hitTest:options: 方法:查找点击位置所对应的虚拟世界中的内容。

显然,hitTest:options: 才是我们需要的。在 3D 世界中的 hit-testing 就像一束激光一样,向点击位置的方向发射,hitTest:options: 的返回值就是被激光穿透的所有卡片的数组。这样就可以检测到用户点击的位置有哪些卡片发生了重叠。

投射

这里简单介绍一下散开的实现原理。SCNSceneRenderer 协议有两个方法用来投射坐标:

  • projectPoint::将三维坐标系中点的坐标,投射到屏幕坐标系中
  • unprojectPoint::将屏幕坐标系中的点的坐标,投射到三维坐标系中

其中屏幕坐标系中的点也是个 SCNVector3,其 z 坐标代表着深度,从 0.0(近裁面)到 1.0(远裁面)。散开的整体过程如下:

散开后,点击空白处会恢复散开的状态,回到初始位置。未参与散开的卡片会被淡化,以突出重点,减少视觉压力。

后台聚类

对于排布比较密集的商家,卡片的重叠现象会很严重。点击散开的卡片数量太多对用户不是很友好。后台在返回用户附近的商家数据时,按照商家的经纬度坐标,使用 K-Means 聚类算法进行二维聚类,将距离很近的商家聚合为一个卡片。由于这些商家的位置大体相同,可以采用一个带有数字的卡片来代表几个商家的位置:

闪烁问题

实测中发现,距离较近的卡片在重叠区域会发生闪烁的现象:

这里要引入一个 3D 渲染引擎普遍要面对的问题——可见性问题。简单来说就是屏幕上哪些物体应该被展示,哪些物体应该被遮挡。GPU 最终应该在屏幕上渲染出所有应该被展示的像素。

可见性问题的一个典型的解决方案就是画家算法,它像一个头脑简单的画家一样,先绘制最远的物体,然后一层层的绘制到最近的物体。可想而知,画家算法的效率很低,绘制较精细场景会很消耗资源。

深度缓冲

深度缓冲

弥补了画家算法的缺陷,它使用一个二维数组来存储当前屏幕中每个像素的深度。如下图所示,某个像素点渲染了深度为 0.5 的像素,并储存该像素的深度:

下一帧时,当另外一个物体的某个像素也在这个像素点渲染时,GPU 会对该像素的深度与缓冲区中的深度进行比较,深度小者被保留并被存入缓冲区,深度大者不被渲染。如下图所示,该像素点下一帧要渲染的像素深度为 0.2,比缓冲区存储的 0.5 小,其深度被存储,并且该像素被渲染在屏幕上:

显然,深度缓冲技术相比画家算法,可以极大地提升渲染效率。但是它也会带来深度冲突的问题。

深度冲突

深度缓冲技术在处理具有相同深度的像素点时,会出现深度冲突(Z-fighting)现象。这些具有相同深度的像素点在竞争中只有一个“胜出”,显示在屏幕上。如下图所示:

如果这两个像素点交替“胜出”,就会出现我们视觉上的闪烁效果。由于每个卡片都被设置了 SCNBillboardConstraint 约束,始终朝向摄像头方向。摄像头轻微的角度变化,都会引起卡片之间出现部分重合。与有厚度的物体不同,卡片之间的深度关系变化很快,很容易出现多个卡片在屏幕同一个位置渲染的情况。所以经常会出现闪烁的现象:

为了解决这 Bug 般的体验,最终决定牺牲深度缓冲带来的渲染效率。SceneKit 为我们暴露了深度是否写入、读取缓冲区的接口,我们将其禁用即可:

plane.firstMaterial.writesToDepthBuffer = NO;plane.firstMaterial.readsFromDepthBuffer = NO;

由于卡片内容内容相对简单,禁用缓冲区对帧率几乎没什么影响。

总结

在到餐业务场景中,以 AR+LBS 的方式展现商家信息,可以给用户带来沉浸式的体验。本文介绍了 ARKit 的一些使用细节,总结了在开发过程中遇到的问题以及解决方案,希望可以给其他开发者带来一点参考价值。

作者简介

  • 曹宇,美团 iOS 开发工程师。2017年加入美团到店餐饮事业群,参与美团客户端美食频道开发工作。

招聘信息

到店餐饮技术部,负责美团和点评两个平台的美食频道相关业务,服务于数以亿计用户,通过更好的榜单、真实的评价和完善的信息为用户提供更好的决策支持,致力于提升用户体验。我们同时承载所有餐饮商户端线上流量,为餐饮商户提供多种营销工具,提升餐饮商户营销效率,最终达到让国人“Eat Better、Live Better”的美好愿景!我们的团队需要经验丰富的FE方向高级/资深工程师和技术专家,欢迎有兴趣的同学投递简历至wangying49#meituan.com。

ARKit:增强现实技术在美团到餐业务的实践相关推荐

  1. 干货好文 | 深度学习是如何在美团点评推荐业务中实践的?

    本文作者:陈文石,2012年毕业于东南大学,美团点评算法专家,目前负责点评平台及综合 BG 推荐平台业务.在加入美团点评之前,曾在百度做过一些个性化推荐相关工作.对人工智能在搜索.推荐等业务应用上有较 ...

  2. 情感分析 | 细粒度情感分析在美团到餐场景中的应用

    每天给你送来NLP技术干货! 来自:美团技术团队 经典的细粒度情感分析(ABSA,Aspect-based Sentiment Analysis)主要包含三个子任务,分别为属性抽取.观点抽取以及属性- ...

  3. 短视频内容理解与生成技术在美团的创新实践

    点击上方"LiveVideoStack"关注我们 美团围绕丰富的本地生活服务电商场景,积累了海量视频数据.如何通过计算机视觉技术用相关数据,为用户和商家提供更好的服务,是一项重要的 ...

  4. 美团技术:设计模式在美团外卖营销业务中的实践

    1.前言 随着美团外卖业务的不断迭代与发展,外卖用户数量也在高速地增长.在这个过程中,外卖营销发挥了"中流砥柱"的作用,因为用户的快速增长离不开高效的营销策略.而由于市场环境和业务 ...

  5. 极客新闻——13、美团到餐研发团队资源成本优化实践

    本文笔记全部来自<极客新闻>--新鲜的技术资讯.权威的趋势剖析.别样的技术洞察 工程师主要面对的是技术挑战,更关注技术层面的目标.研发团队的管理者则会把实现项目成果和业务需求作为核心目标. ...

  6. 广告深度预估技术在美团到店场景下的突破与畅想

    猜你喜欢 1.如何搭建一套个性化推荐系统? 2.某视频APP推荐详解(万字长文) 3.微博推荐算法实践与机器学习平台演进 4.腾讯PCG推荐系统应用实践 5.强化学习算法在京东广告序列推荐场景的应用 ...

  7. 共享技术红利 美团点评全面开放云能力并发布三大类AI产品

    随着移动互联网的发展,美团外卖.猫眼电影.大众点评等应用为人们带来了极大的便利.打开APP下单.付款.评价,简单的操作背后实离不开精准的算法与强大的云平台支撑.美团点评提供的不只是生活服务,更是其多年 ...

  8. 增强现实技术的9个最新应用趋势

    来源:资本实验室 摘要:随着AR技术的成熟,AR与行业的融合越来越深入.从设计到营销,从教育到医疗,从出行到文化,AR正在重新定义各产业的思维方式和运行方式. 1.教育 AR应用程序正在以更具互动性的 ...

  9. 点餐业务服务拆分分析

    我们有两种方式可以拆分服务,第一种我们的系统分为买家端和卖家端,你可以把vue放到app上,用来做买家端需要的接口,卖家端呢,也就是PC端,由freemark做的html页面作为另外一个边缘服务,两端 ...

最新文章

  1. Kali Linux”下列签名无效“解决办法
  2. 题目1171:C翻转
  3. 利用pip3安装包只能在python2中调用
  4. 【考前必知】软考考前注意事项
  5. java 性能 排序_Java常用排序算法及性能测试集合
  6. STM32三种启动模式 boot0 boot1
  7. oracle数据库自动备份脚本
  8. JAVA I/O基本操作
  9. 5. Web 的结构组件
  10. 学了这几招,社交时人人都对你刮目相看
  11. Fliqlo:一款精致高逼格的翻页时钟屏保软件
  12. win7配置计算机失败怎么办,Win7配置失败还原更新怎么回事 Win7配置update失败的解决办法...
  13. 在你的时区里,一切准时-很美的英文诗
  14. 阿里云服务器租赁注意点
  15. mysql如何使用多核cpu_利用多核 CPU 实现并行计算
  16. select函数用法详解
  17. u盘安装grub linux,U盘安装的引导搞错了
  18. 分享一下自己做电影解说的步骤流程和经验,小白必看!
  19. css中的min-height、max-height、min-width、max-width
  20. BGP机房-产品介绍

热门文章

  1. Autodesk云计算系列视频 --- 云计算与Civil 3D
  2. Postgres主进程文件—postmaster.pid
  3. 用 Shell 快速写一个嵌入式测试框架
  4. Linux 块设备,Block Layer层架构演变
  5. 服务器分虚拟空间,服务器怎样分虚拟主机
  6. PackagesNotFoundError: The following packages are not available from current channels:
  7. 可怕的乖孩子_当今的中国,有句很可怕的话:所有的乖孩子注定不幸福!
  8. LeetCode MySQL 1777. 每家商店的产品价格(行列转换)
  9. LeetCode MySQL 180. 连续出现的数字(cast)
  10. LeetCode MySQL 1454. 活跃用户(连续dense_rank排名函数)