剩下的人很少能对增强现实(AR)感到惊讶。 对于某些人来说,该技术与玩具关联了几个小时。 其他人发现它更实用。
我叫Dmitry,我正在为iOS开发Yandex.Maps。 今天,我将告诉Habr的读者我们如何使用增强现实技术创建路由。 您还将了解使用ARKit框架的功能,因此,增强现实的引入不再是计算机视觉领域的专家所关心的。

2009年,《 Esquire》杂志是第一家在其产品中添加增强现实支持的媒体。 在杂志的封面上张贴了一个代码,您可以通过该代码看到小罗伯特·唐尼(Live Down)。

娱乐业中AR的使用不限于此。 一个生动的例子是2016年发行的游戏Pokemon Go。 到当年7月,它已被下载超过1600万次。 游戏的成功导致了许多带有AR的克隆的出现。
近年来,AR行业中的重大事件可视为Google Glass和Microsoft Hololens的发布。 这种设备的出现显示了大公司迁徙的媒介。
苹果也不例外。 在2017年,该公司推出了ARKit框架,该框架对业界的重要性几乎不可高估。 我们将更详细地讨论它。
ARKit
ARKit的功能,使其易于使用AR:
- 不需要特殊标签(标记),
- 与现有的Apple 2D / 3D图形框架集成-SceneKit,SpriteKit,Metal,
- 在确定设备在太空中的位置和方向时精度很高,
- 无需校准相机或传感器。
ARKit的内部是一个视觉惯性测距系统,该系统将数据与设备的视觉(摄像头)和惯性(加速度计,陀螺仪)子系统相结合,以确定舞台上的位置和位移。 该系统的连接元素是卡尔曼滤波器-一种算法,该算法在每个时间选择两个子系统的最佳读数,并以我们在舞台上的位置和方向的形式提供给我们。 ARKit还对场景有一个“了解”-我们可以定义水平和垂直表面以及场景的照明条件。 因此,在将对象添加到场景时,我们可以为其添加默认照明,从而使该对象看起来更逼真。
顺便说一句很快,将发布2.0版框架,其中将添加新功能并显着提高定位精度。
ARKit允许开发人员将高质量的增强现实嵌入到他们的应用程序中,同时花费更少的精力。 我们将使用Yandex.Maps的示例进行演示。
在Yandex.Maps上使用AR进行路由
通常,在发布新版本的iOS之后,Yandex的许多团队会聚在一起讨论将新功能引入其应用程序的可能性。 Yandex.Mart团队也是如此。 自发布ARKit以来的一个月内,我们经常讨论如何在Maps中实现它。 我们没有听到彼此有什么样的想法! 很快,我们得出的结论是,最有用的表面解决方案之一是在路由中使用增强现实。
之所以选择这种想法,是因为许多卡用户经常会遇到一种情况,当您发现自己不熟悉该区域时,您需要快速决定前往何处。 普通地图用户的标准方法是打开应用程序,建立人行路线,然后转身确定要移动的位置。 将增强现实技术引入行人路线的想法是使用户免于不必要的操作,立即显示您需要直接在摄像机图像上方移动的位置。
首先,我想谈一谈路由。 我对这个概念有什么看法? 从移动应用程序的实现角度来看,这是一组相当标准的步骤,允许用户从A点到达B点:
- 选择出发点和到达点,
- 以地理(纬度,经度)坐标中的一组点的形式接收路线,
- 在路线图上显示
- 沿路线移动时,为用户提供其他信息。
我们不会只关注前两点。 我只能说,我们是通过跨平台库Yandex.Mapkit获得路线的,该库也可以pod的形式提供给您。 增强现实路线与地图中的标准路线有何不同? 首先,主要区别是几乎完全隐藏的地图。 主要重点放在屏幕区域上,其中包含来自摄像机的视频流的图像,在该区域上叠加了其他视觉元素(结束标记,辅助标记和路线图像)。 这些视觉元素中的每一个都有自己的语义负载和逻辑(何时以及如何显示)。 稍后,我们将更详细地考虑每个元素的作用,但是现在,我建议考虑摆在我们面前的任务:
- 学习了解对象在ARKit场景上的位置,了解它们的地理坐标,
- 了解如何在3D场景上以足够的性能绘制必要的UI。
我们需要将点的坐标从地理坐标转换为舞台上的坐标,选择要显示的点,并在相机图像顶部的正确位置显示所有必需的UI。 但是事实证明,所有事情都比乍看起来要复杂一些。
在直接开始实现这些功能之前,我的一位同事承担了制作原型的任务,该原型显示了使用一组可访问的工具来实现类似功能的可能性(或不可行)。 两个星期以来,我们看着San Sanych手持电话在开放空间的开放空间和办公室附近环境中耕种,并通过相机的棱镜观察周围的世界。 结果,我们得到了一个可以正常工作的原型,该原型将路线的每个点显示为舞台上的标记并与之保持一定距离。 在这个原型的帮助下,在成功组合的情况下,有可能下班到地铁,几乎不会迷路。 但是,很认真的说,他确认了实现预期功能的可能性。 但是,我们的团队仍然需要解决许多任务。
一切始于工具的研究。 当时,团队中只有一个人有使用3D图形的经验。 让我们快速看一下那些想用ARKit实现这种想法的人所必须使用的工具。
工具和API
渲染对象的主要工作是创建和管理SceneKit框架场景对象。 随着ARKit的问世,ARSCNView类(SCNView类的后代-用于SceneKit中场景的基类)可供开发人员使用,它解决了集成ARKit和SceneKit的大部分耗时任务,即:
- 手机在太空中的位置与摄像机在舞台上的位置同步,
- 场景的坐标系与ARKit坐标系重合,
- 作为场景的背景,使用了来自设备相机的视频流。
ARSCNView对象还为开发人员提供了增强现实会话对象,可以使用委托对象以必要的配置启动,停止或预订各种事件。
要将对象添加到场景中,可以使用继承对象或直接使用SCNNode对象。 此类表示其父级坐标系中的位置(三维矢量)。 因此,我们在场景上得到一棵对象树,在特殊对象(场景的rootNode)中有一个根。 这里的一切与UIKit中的UIView对象的层次结构非常相似。 SCNNode对象在添加材质和照明时可以显示在舞台上。
为了将增强现实技术添加到移动应用程序中,您还需要了解ARKit API的主要对象。 主要的是增强现实会话的对象-ARSession。 该对象执行数据处理,并负责增强现实会话的生命周期。 本文的目的不是重述ARKit和SceneKit的文档,因此,我将不写增强现实会话的所有可用配置参数,而是着重介绍导航应用程序增强现实会话的配置参数中最重要的一个-worldAlignment。 此参数确定会话初始化时场景轴的方向。 通常,在初始化增强现实会话时,ARKit会创建一个坐标系统,该坐标系统的起点与手机在空间中的当前位置重合,并根据woldAlignment属性的值来定向该系统的轴。 在我们的实现中,使用了gravityAndHeading值,这意味着轴将如下定向:Y轴(与重力相反的方向),Z轴(向南)和X轴(向东)。

在多种情况下,X / Z轴确实会与向南/向东的方向对齐,但是由于指南针读数错误,这些轴可能会与文档中所述的方向成一定角度。 这是我们必须处理的问题之一,但稍后会进一步讨论。
现在,我们已经研究了基本工具,下面总结一下:使用SceneKit映射路线是将SCNNode对象添加到场景中的位置,该位置是通过从地理坐标转换为场景坐标而获得的。 在讨论坐标转换以及通常在场景中放置对象之前,我们先假设假定我们知道对象在舞台上的位置,然后再讨论渲染UI元素的问题。
终点标记
具有增强现实的行人路线的主要视觉元素是终点标记,它显示路线的终点。 同样在标记上方,我们向用户显示了到路线终点的距离。

尺码
当我们第一次看到此标签的设计时,我们首先注意了此标签尺寸的要求。 他们没有遵守透视投影的规则。 我将解释在用于创建例如计算机游戏的三维引擎中,“外观”是使用透视投影建模的。 根据透视投影的规则,以较小的比例描绘远处的对象,并且平行线通常不平行。 因此,随着照相机远离场景上的物体,物体在屏幕平面上的投影尺寸线性变化(减小)。 从布局的描述可以得出,当屏幕上的标记尺寸小于50 m时,其尺寸具有固定(最大)大小,然后从50 m线性减小至2 km,之后最小尺寸保持不变。 这样的要求显然是由于用户方便。 它们使用户永远不会丢失视线的终点,因此用户始终会知道要移动到哪里。

我们必须了解如何将自己置入根据某些规则工作的SceneKit投影机制中。 我想马上指出,我们大约有两周时间来完成所有事情,因此根本没有时间对解决所提出问题的各种方法进行深入分析。 现在,分析我们的决策,评估它们变得更加简单,并且我们可以得出结论,大多数决策都是正确的。 实际上,对大小的要求是第一个绊脚石。 可以使用SceneKit和UIKit来解决下面列出的所有问题。 我试图详细解释如何使用这两种方法解决每个问题。 使用哪种方法取决于您。
假设我们决定使用SceneKit实现结束标签。 如果考虑到根据布局的标签在屏幕上看起来应该像圆形,那么很明显,在SceneKit中,标签对象应该是一个球体(因为该球体在任何平面上的投影都是一个圆)。 为了使投影在屏幕上具有设计者要求中定义的特定半径,有必要知道每个时间点的球体半径。 因此,通过将某个半径的球体放置在场景中的某个点上,并在接近或移动时不断更新其半径,我们可以随时在屏幕上获得所需大小的投影。 确定任意时间点的球体半径的算法如下:
- 定义物体在舞台上的位置-球的中心,
- 找到此点在屏幕平面上的投影(使用SceneKit API),
- 为了确定屏幕上标记的所需大小,我们找到了相机到舞台上球体中心的距离,
- 我们使用设计中所述的规则,根据到物体的距离确定屏幕上所需的尺寸,
- 知道屏幕上标记的大小(圆的直径)后,我们选择该圆上的任意点,
- 对所选点进行反向投影(unprojectPoint),
- 我们找到从舞台上的接收点到球体中心的向量长度。
向量长度的获得值将是所需的球体半径。

在实施时,我们无法找到确定场景中对象大小的方法,因此我们决定使用UIKit绘制完成标记。 在这种情况下,算法会重复执行步骤1-5,然后使用UIKit工具在屏幕上绘制所需大小的圆,并以在步骤2中获得的点为中心。 在此处可以找到使用UIKit的标签的示例实现。
关于代码的几句话在文章的结尾,我提供了一些指向有用且简单有趣的材料的链接,包括示例,您可以在其中详细查看解决本文中所提出的问题并实现所提出的算法的真实代码。 在我看来,主要的兴趣在于行人选路的原型,该原型集合了所有功能,但轴调整机制除外,下文将对此进行详细介绍。
上面的代码不声称是最优性,完整性和生产质量=)
在这种情况下,使用SceneKit和UIKit的区别还在于,当在SceneKit上实现时,将使用材质和几何形状创建用于路线终点(终点标记)的SCNNode对象,因为在使用UIKit时必须可见我们仅需要node对象来搜索在屏幕平面上的投影(以确定屏幕上标记的中心)。 在这种情况下,不需要添加几何形状和材料。 请注意,可以通过两种方式找到从摄像机到路线端点的SCNNode对象的距离-使用这些点的地理坐标,或者将其用作场景上这些点之间的向量的长度。 这是可能的,因为相机对象是SCNNode属性。 要获取相机节点,您需要引用场景的pointOfView属性。
我们学习了如何在SceneKit上实现时确定任意时间点的终点标记节点的半径,以及如何在UIKit上实现来确定终点标记视图的位置。 仍然需要了解何时需要更新这些值? 这个地方是SCNSceneRendererDelegate对象方法:
renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval)
在每个渲染的场景帧之后调用此方法。 通过更新此方法主体中的属性值,我们将获得正确显示的完成标签。
动画制作
完成标记出现在dev中之后,我们继续向该标记添加波纹动画。 我认为对于大多数iOS开发人员而言,创建动画并不重要。 但是在考虑实现方法时,我们遇到了不断更新我们的观点框架的问题。 请注意,在大多数情况下,动画会添加到静态UIView对象中。 一个类似的问题-使用SceneKit实现时,会不断更新节点几何形状的半径。 事实是,脉动动画可归结为圆形大小(对于UIKit)和球体半径(对于SceneKit)的动画。 是的,是的,我们知道在UIKit中可以使用CALayer完成这种动画,但是为了简化讲故事,我决定针对两个框架对称地考虑此问题。 考虑在UIKit上的实现。 如果将用于对同一框架进行动画处理的代码添加到更新视图框架的现有代码中,则将通过显式设置框架来中断动画。 因此,作为解决此问题的方法,我们决定使用UIView对象的transform.scale.xy属性的动画。 使用SceneKit实现时,必须将scale属性的动画添加到SCNNode对象。 在这种情况下使用SceneKit的好处是它完全支持CoreAnimation,因此无需学习新的API。 实现类似于Yandex.Maps中的标签动画的动画的代码如下所示:
let animationGroup = CAAnimationGroup.init() animationGroup.duration = 1.0 animationGroup.repeatCount = .infinity let opacityAnimation = CABasicAnimation(keyPath: "opacity") opacityAnimation.fromValue = NSNumber(value: 1.0) opacityAnimation.toValue = NSNumber(value: 0.1) let scaleAnimation = CABasicAnimation(keyPath: "scale") scaleAnimation.fromValue = NSValue(scnVector3: SCNVector3(1.0, 1.0, 1.0)) scaleAnimation.toValue = NSValue(scnVector3: SCNVector3(1.2, 1.2, 1.2)) animationGroup.animations = [opacityAnimation, scaleAnimation] finishNode.addAnimation(animationGroup, forKey: "animations")
广告牌
在本文的开头,我提到了一个广告牌,该广告牌与路线的终点有一段距离,从本质上讲,这是一个标签,其文字始终位于终点标记上方。 按照传统,我将概述UIKit和SceneKit的实现所固有的问题,并介绍每种框架的可能解决方案。
让我们从UIKit开始。 在这种情况下,广告牌是常规的UILabel,其中的文本会不断更新,显示到路线终点的距离。 让我们看看我们面临的问题。

如果您在框架上设置标签,然后旋转手机,我们会发现框架没有变化(如果没有变化,这很奇怪)。 同时,我们希望标签保持与地球平面平行。

我想每个人都知道,当更改设备的方向时,我们需要旋转标签,但角度是多少? 如果您打开想象力并想象此过程中涉及的所有坐标系和矢量轴,我们可以得出结论,旋转角度等于UIKit坐标系的x轴与SceneKit坐标系的X轴在屏幕平面上的投影之间的角度。

一项简单的任务再次证明了学校几何课程的实用性。
当使用SceneKit实现完成标记时,您很可能需要使用SceneKit工具以一定距离渲染广告牌,这意味着您肯定有使SCNNode对象始终对准摄像机的任务。 我认为,如果您查看图片,问题将变得更加清楚:

通过使用SCNBillboardConstraint API可以解决此问题。 将一个带有自由Y轴的常量添加到节点的基板集合中,我们得到一个绕其坐标系的Y轴旋转的节点,以便始终朝着相机定向。 开发人员的唯一任务是将该节点放置在正确的高度,以使带有该距离的广告牌始终对用户可见。
let billboardConstraint = SCNBillboardConstraint() billboardConstraint.freeAxes = SCNBillboardAxis.Y finishNode.constraints = [billboardConstraint]
助理标签
具有增强现实功能的行人路线的主要特征之一是,在团队内部,我们考虑了辅助标记-一种特殊的视觉元素,当路线的终点离开可见性区域时会出现在屏幕上,并向用户显示在哪里转动手机,以便该标记出现在屏幕上终点线。

我敢肯定,许多读者在某些游戏(大多数是射击游戏)中会遇到类似的功能。 当我们在布局中看到此UI元素时,我们的团队感到惊讶。 我必须马上说,正确实现这种功能可能需要您进行一个多小时的试验,但是最终结果值得花时间。 我们首先定义需求,即:
- 对于设备的任何方向,标签都沿着屏幕的边界移动,
- 如果用户已将其旋转至路线终点180度,则标签会显示在屏幕底部,
- 在任何时候,转向标记都是到路线终点的最短转弯。
在描述了需求之后,我们开始实施。 几乎立即,我们得出的结论是,将使用UIKit完成渲染。 实施的主要问题是在每个时刻都确定该标签的中心。 在考虑完成标记之后,这样的任务应该不会造成困难,因此,我不会详细介绍它的解决方案。 在本文中,我将仅描述选择辅助标签中心的算法,并且可以在此处找到源代码。
搜索中心算法搜索算法:
- 为路线终点创建一个SCNNode对象,并在场景中从该点的地理坐标获取位置,
- 找到一个点在屏幕平面上的投影,
- 在屏幕坐标系中找到从屏幕中心到找到的投影点的线段与屏幕边界线段的交点。

找到的交点是辅助标记的所需中心。 通过类似于更新结束标签参数的代码,我们将呈现辅助标签的代码放置在上面已经说明的委托方法中。
路线折线
建立了一条路线并在屏幕上看到终点标记后,用户可以通过仅沿标记的方向定向来到达终点,但之所以称为路线,是因为它向用户显示了路线。 我们认为减少行人选路的功能(从AR版本中排除路线显示)会很奇怪。 为了可视化路线,决定显示一组沿其移动的箭头。 在这种情况下,设计人员感到满意的是,箭头在移开时实际上会消失(大小由透视投影的规则确定),因此决定使用SceneKit进行实施。
在继续描述实现之前,重要的是要注意,根据设计,箭头之间的距离应为3 m。 如果您估计需要使用约1公里长的路线渲染的对象(箭头)的数量,则大约为330件。 同时,向每个对象添加沿其路径部分的运动动画。 请注意,远离摄像机在舞台上大约100-150米的位置的箭头实际上由于尺寸小而看不见。 考虑了这些因素后,决定不显示所有对象,而是仅显示沿路线距离用户不超过100米的那些对象,并定期更新显示的对象集。 我们显示足够的视觉信息,从而消除了不必要的SceneKit计算并节省了用户的电量。

让我们看看为了获得最终结果而必须实现的主要步骤:
- 选择要显示其原语的路径部分,
- 创建3D模型,
- 动画创作
- 沿路线行驶时更新。
选择要显示的图
如上所述,我们不会为整个路线显示箭头,而是选择要显示的最佳区域。 在任意时间点选择路段包括搜索到用户当前位置的最近路线路段(路线是路段/路段的序列),然后选择从最近路线到路线终点的路段,直到其总长度超过100米。

3D模型创建
让我们更详细地考虑创建3D模型的过程。 在大多数情况下,创建一个简单的3D模型(如我们的箭头所示)所需要做的就是打开任何3D编辑器,花一些时间来掌握它并在其中创建这个模型。 如果您团队中的人有3D建模经验,或者他们有时间学习3DMax(必须购买),那么您真是幸运。 不幸的是,在实施此功能时,我们谁都没有任何特殊经验,没有空闲的培训时间,因此我们不得不用一种即兴的手段来制作模型。 我的意思是在代码中对模型的描述。 一切始于以三角形形式呈现3D模型。 然后,我们必须在模型的坐标系中手动找到这些三角形的顶点的坐标,然后创建三角形的顶点的索引数组。 利用这些数据,我们可以直接在SceneKit中创建必要的几何图形。 您可以创建类似于我们的模型,例如,如下所示:
class ARSCNArrowGeometry: SCNGeometry { convenience init(material: SCNMaterial) { let vertices: [SCNVector3] = [ SCNVector3Make(-0.02, 0.00, 0.00), // 0 SCNVector3Make(-0.02, 0.50, -0.33), // 1 SCNVector3Make(-0.10, 0.44, -0.50), // 2 SCNVector3Make(-0.22, 0.00, -0.39), // 3 SCNVector3Make(-0.10, -0.44, -0.50), // 4 SCNVector3Make(-0.02, -0.50, -0.33), // 5 SCNVector3Make( 0.02, 0.00, 0.00), // 6 SCNVector3Make( 0.02, 0.50, -0.33), // 7 SCNVector3Make( 0.10, 0.44, -0.50), // 8 SCNVector3Make( 0.22, 0.00, -0.39), // 9 SCNVector3Make( 0.10, -0.44, -0.50), // 10 SCNVector3Make( 0.02, -0.50, -0.33), // 11 ] let sources: [SCNGeometrySource] = [SCNGeometrySource(vertices: vertices)] let indices: [Int32] = [0,3,5, 3,4,5, 1,2,3, 0,1,3, 10,9,11, 6,11,9, 6,9,7, 9,8,7, 6,5,11, 6,0,5, 6,1,0, 6,7,1, 11,5,4, 11,4,10, 9,4,3, 9,10,4, 9,3,2, 9,2,8, 8,2,1, 8,1,7] let geometryElements = [SCNGeometryElement(indices: indices, primitiveType: .triangles)] self.init(sources: sources, elements: geometryElements) self.materials = [material] } } static func arrowBlue() -> SCNGeometry { let material = SCNMaterial() material.diffuse.contents = UIColor.blue material.lightingModel = .constant return ARSCNArrowGeometry(material: material) }
最终结果如下所示:

路线动画
显示路线的动画线的下一步是创建动画本身的阶段。 但是,实现动画的方式是什么(在最终形式中,它看起来好像箭头在路线的所选部分的起点开始移动,并沿该路线的“浮动”方向到达该部分的结尾)?
我不会描述创建这种动画的所有可能方法,而是将更详细地介绍我们选择的方法。 选择路线的一部分后,我们将其分为相同长度的部分-一个箭头动画的部分。 每个此类部分均以彩色突出显示,并且长度等于箭头之间的距离。

在每个部分的开头,我们创建箭头的SCNNode对象,该对象的动画包括沿其部分移动。

如您所见,动画部分有时包含一个片段,有时包含两个或多个片段。 这完全取决于箭头与构成路线的点的坐标之间的距离(在我们的情况下为3米)。
箭头动画是两个步骤的序列:
- 在初始位置具有初始旋转角度的外观,
- 沿线段的一系列偏移,并在线段的连接点处旋转。
从示意图上看,它看起来像这样:

在我们看来,这似乎是使用SCNAction API(一种声明性API)实现这种动画的最简单方法,它使您可以方便地创建连续,分组和重复的动画。 您可以在此处更详细地查看实现 。 由于每个箭头都在下一个箭头的动画部分的起点处结束其动画,因此会产生箭头沿路线的整个选定部分连续移动的印象。
在此基础上,我建议完成对渲染各个方面的考虑,并转到主要部分-通过对象的地理坐标确定对象在舞台上的位置。
确定物体在场景中的位置
我们将开始讨论有关通过考虑坐标系来确定对象在场景中的位置的问题,坐标系之间必须进行转换。 其中只有2个:
- 测地线(或为简化起见,为地理)坐标-现实世界中物体(路线点)的位置,
- 笛卡尔坐标-场景中对象的位置(在ARKit中)。 回想一下,场景的坐标系与坐标系ARKit一致(在使用ARSCNView的情况下)。
由于ARKit中的坐标以米为单位进行测量,因此可以将一个坐标系转换为另一坐标系,反之亦然,并且可以将两个大地坐标之间的偏移量高精度地转换为ARKit坐标系的X和Z轴在很小的偏移量下以米为单位的偏移量。 让我提醒您,大地坐标是具有一定经度和纬度的点。
让我们回顾一下地理学中诸如平行线和子午线之类的重要概念及其基本属性:
- 平行线是具有纬度值的线。 各种平行线的长度是不同的。
- 子午线 -具有经度值的线。 所有子午线的长度都相同。
现在让我们看看如何计算两个大地坐标之间的偏移量(以米为单位)
和
:
, 
, 
解说大地坐标中的位移仅在小位移时才线性映射到米。 大排量时,有必要诚实地进行积分。
现在我们可以将偏移量从一个坐标系转换为另一个坐标系,我们需要确定一个参考点-一个同时知道地理坐标和ARKit中的坐标(舞台上的坐标)的点。 找到这样的点后,我们可以确定舞台上任何对象的坐标,知道其地理坐标并使用上述公式。
为了清楚起见,请考虑一个示例:
在增强现实会议开始时,我们要求CoreLocation提供我们的地理坐标,并立即收到它-
。 回想一下ARKit坐标系的原点是在会话开始时在设备所处的位置这一事实,我们获得了参考点,因为我们知道了地理坐标和场景中的坐标
。 让我们需要在具有地理坐标的对象的场景中找到坐标
。 为此,请找到对象的地理坐标与参考点的地理坐标之间的以米为单位的偏移量,然后将找到的偏移量添加到参考点场景中的坐标上。 场景中所得的坐标将是所需的坐标。

我注意到,仅当场景坐标系的X / Z轴与向南/向东的方向对齐时,以这种方式找到的场景上的位置才会与对象在现实世界中的位置相对应。 理论上,应该通过将worldAlignment标志设置为gravitiAndHeading来实现轴对齐。 但是正如我在帖子开头所说的那样,情况并非总是如此。
让我们更详细地考虑确定原点的方法。 为此,我们引入估计的概念-一组地理坐标和舞台上的坐标。

上面提出的用于确定参考点的方法可能不会始终使用。 在增强现实会话开始时,可能不会立即执行对用户的CLLocation的请求,此外,所获得的坐标的精度可能具有较大的误差。 当我们从CoreLocation获取值时,向SceneKit询问舞台上的位置会更正确。 在这种情况下,确实可以同时获得最终估算的组成部分,我们有机会将任何估算用作参考点。 使用ARKit时,偏移误差会随着时间累积,因此Apple不建议使用ARKit作为导航工具。
当我们决定用增强现实技术实现行人路由时,我们对当时存在的解决方案进行了一些研究,使用ARKit完成类似任务,并遇到了ARKit + CoreLocation框架。 该框架的思想是,借助ARKit,与仅使用CoreLocation相比,我们可以更准确地确定用户的位置。
ARKit + CoreLocation概念:
- 从CLLocationManager接收CLLocation时
- 使用scene.pointOfView.worldPosition请求在场景上的位置
- 将此坐标对(估计)保存到缓冲区
- 获取必要的确切位置
- 选择最佳估计
- 计算舞台上当前位置与最佳估计值在舞台上的位置之间的偏移量
- 将找到的偏移量应用于最佳估计的地理坐标
, , CoreLocation, .
, « ». , .
(, ):
- ( horizontalAccuracy),
- ,
- 100 .
CoreLocation . , , CoreLocation , 100 .
, . , , ( 100 ).
, X/Z ARKit / . ARKit , , .
怎么了, (, IKEA, ), Y ARKit – , . gravity worldAlignment.
, . , , , . . AR . , , , , . AR.
, . ,
CLLocationManager
—
。
CLLocationManager —
相应地。
ARKit —
2 CoreLocation
。
。 , CoreLocation . . ARKit /.

ARKit Y? . :
- ,
- ,
- ,
- ,
- .
. . CLLocationManager' , ( ), ( ).
1, 2 :
和
,
– 2, ARKit.
( Bearing).

. , ? , , , , . , , , horizontalAccuracy. , , , . :

, , .
. , . 例如:
, , , , (), . , . , , . , , ( ). .
, . , , ( , , ).
测试中
, . , , , . 2 :
- , , , , .
. , , 100 CLLocation, . , , , 10 ( 10 ). ? , "". , . , , , . , , . , CoreLocation. , . , .
. , . , (, ), , 0 . , , .
" ". . , , , , CLLocation, , . ( ) .
, ARKit.

, .

( 3-4 ) , .

JS, AR CoreLocation.

— gravity worldAlignment . , . .
而不是结论
Slack, , , , . AR. . AR AppStore 2017 . , .
有用的链接
, . .