郑州网站seo哪家公司好,网站免费制作教程,网页界面设计以什么为载体,游戏定制公司前言 增强现实#xff08;Augmented Reality#xff09;是一种在视觉上呈现虚拟物体与现实场景结合的技术。Apple 公司在 2017 年 6 月正式推出了 ARKit#xff0c;iOS 开发者可以在这个平台上使用简单便捷的 API 来开发 AR 应用程序。 本文将结合美团到餐业务场景#xff… 前言 增强现实Augmented Reality是一种在视觉上呈现虚拟物体与现实场景结合的技术。Apple 公司在 2017 年 6 月正式推出了 ARKitiOS 开发者可以在这个平台上使用简单便捷的 API 来开发 AR 应用程序。 本文将结合美团到餐业务场景介绍一种基于位置服务LBS的 AR 应用。使用 AR 的方式展现商家相对用户的位置这会给用户带来身临其境的沉浸式体验。下面是实现效果 项目实现 iOS 平台的 AR 应用通常由 ARKit 和渲染引擎两部分构成 ARKit 是连接真实世界与虚拟世界的桥梁而渲染引擎是把虚拟世界的内容渲染到屏幕上。本部分会围绕这两个方面展开介绍。 ARKit ARKit 的 ARSession 负责管理每一帧的信息。ARSession 做了两件事拍摄图像并获取传感器数据对数据进行分析处理后逐帧输出。如下图 设备追踪 设备追踪确保了虚拟物体的位置不受设备移动的影响。在启动 ARSession 时需要传入一个 ARSessionConfiguration 的子类对象以区别三种追踪模式 ARFaceTrackingConfigurationARWorldTrackingConfigurationAROrientationTrackingConfiguration其中 ARFaceTrackingConfiguration 可以识别人脸的位置、方向以及获取拓扑结构。此外还可以探测到预设的 52 种丰富的面部动作如眨眼、微笑、皱眉等等。ARFaceTrackingConfiguration 需要调用支持 TrueDepth 的前置摄像头进行追踪显然不能满足我们的需求这里就不做过多的介绍。下面只针对使用后置摄像头的另外两种类型进行对比。 ARWorldTrackingConfiguration ARWorldTrackingConfiguration 提供 6DoFSix Degree of Freedom的设备追踪。包括三个姿态角 Yaw偏航角、Pitch俯仰角和 Roll翻滚角以及沿笛卡尔坐标系中 X、Y 和 Z 三轴的偏移量 不仅如此ARKit 还使用了 VIOVisual-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 的追踪能力受限官方文档不推荐直接使用。但是鉴于 对三个姿态角的追踪已经足以正确的展现商家相对用户的位置了。ARWorldTrackingConfiguration 的高精度追踪更适合于距离较近的追踪。比如设备相对桌面、地面的位移。但是商家和用户的距离动辄几百米过于精确的位移追踪意义不大。ARWorldTrackingConfiguration 需要规范用户的操作、确保环境光线良好。这对用户来说很不友好。最终我们决定使用 AROrientationTrackingConfiguration。这样的话即便是在夜晚甚至遮住摄像头商家的位置也能够正确的进行展现。而且剧烈晃动带来的影响很小商家位置虽然会出现短暂的角度偏差但是在传感器数值稳定下来后就会得到校准。 坐标轴 ARKit 使用笛卡尔坐标系度量真实世界。ARSession 开启时的设备位置即是坐标轴的原点。而 ARSessionConfiguration 的 worldAlignment 属性决定了三个坐标轴的方向该属性有三个枚举值 ARWorldAlignmentCameraARWorldAlignmentGravityARWorldAlignmentGravityAndHeading三种枚举值对应的坐标轴如下图所示 对于 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 给我们提供了三种可选的引擎 MetalSpriteKitSceneKit强大的 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; 由于卡片内容内容相对简单禁用缓冲区对帧率几乎没什么影响。 总结 在到餐业务场景中以 ARLBS 的方式展现商家信息可以给用户带来沉浸式的体验。本文介绍了 ARKit 的一些使用细节总结了在开发过程中遇到的问题以及解决方案希望可以给其他开发者带来一点参考价值。 作者简介 曹宇美团 iOS 开发工程师。2017年加入美团到店餐饮事业群参与美团客户端美食频道开发工作。招聘信息 到店餐饮技术部负责美团和点评两个平台的美食频道相关业务服务于数以亿计用户通过更好的榜单、真实的评价和完善的信息为用户提供更好的决策支持致力于提升用户体验。我们同时承载所有餐饮商户端线上流量为餐饮商户提供多种营销工具提升餐饮商户营销效率最终达到让国人“Eat Better、Live Better”的美好愿景我们的团队需要经验丰富的FE方向高级/资深工程师和技术专家欢迎有兴趣的同学投递简历至wangying49#meituan.com。