ARKit 初探

0. 前言

作为一名刚入门的 iOS 开发者,前阵子稍稍研究了一下最新发布的 ARKit,然后结合几个其他开源项目做成了一个 ARGitHubCommits。前天在上海第 8 次 T 沙龙上分享了一个《ARKit 初探》的 topic,现在将它写成文章,以便浏览。

1. ARKit 简介

下面是苹果开发者官网 ARKit 页面的一段介绍:

iOS 11 引入了新的 ARKit 框架,让您轻松创建无可比拟的 iPhone 和 iPad 增强现实体验。 通过将数字对象和信息与您周围的环境相融合,ARKit 为 App 解开了屏幕之缚,带领着它们跨越屏幕的界限,让它们以全新的方式与现实世界交流互动。

可见,苹果在 AR 的市场上应该是做了很多准备。不仅有本文要介绍的 ARKit,在最新发布的 iPhone X 中也对摄像头做了优化,配备了前置景深摄像头,将 AR 和面部识别结合起来。所以,AR 可能将会是未来几年内的一个重要发展方向。

2. 设备要求

苹果在硬件上也做了一些努力。我们可以从官网的介绍中得出以下几个信息:

  • 建立在优秀的硬件设施和算法上,ARKit 采集的现实世界数据相对来说比较精准(比 Google 的 ARCore 要好些)。
  • 可以利用 ARKit 来探测水平面,然后可以在水平面上放置小的物体。
  • ARKit 会根据周围光线的亮度自动调节虚拟物体的亮度和阴影、纹理等信息。

当然,要运行 ARKit,在硬件上也有一些要求。一定是要具备 A9 及以上的处理器(iPhone 6s 为 A9 处理器)的设备才可以运行 AR。软件上,如果要开发 ARKit App,那么要有 Xcode 9 和 iOS 11 SDK。

当你做好了一切准备,那就让我们进入 ARKit 的世界!

3. AR 工作流程

AR 工作流程

上图解释的是 ARKit 的工作流程。其中蓝色表示 ARKit 负责的部分,绿色表示 SceneKit 负责的部分。当然,建立虚拟世界也可以使用其他的框架,比如 SpriteKit、Metal,本文将以 SceneKit 为例子进行讲解。

  1. 首先,ARKit 利用摄像头拍摄现实场景的画面,然后 SceneKit 用来建立虚拟世界。
  2. 建立好了以后,ARKit 负责将现实世界和虚拟世界的信息融合,并渲染出一个 AR 世界。
  3. 在渲染的同时,ARKit 要负责以下三件事:
  • 维持世界追踪
    指的是当你移动摄像头,要去获取新的现实世界的信息。
  • 进行场景解析
    指的是解析现实世界中有无特征点、平面等关键信息。
  • 处理与虚拟世界的互动
    指的是当用户点击或拖动屏幕时,处理有没有点击到虚拟物体或者要不要进行添加/删除物体的操作。

由此可见,ARKit 主要做的事是:捕捉现实世界信息、将现实和虚拟世界混合渲染、并且时刻处理新的信息或者进行互动

理解了 AR 的工作流程后,让我们来看看 ARKit 中一些重要的类的职责。

4. ARKit 和 SceneKit 关系图

ARKit 和 SceneKit 关系图

上面是 ARKit 和 SceneKit 的关键的类的关系图。其中 ARSCNView 是继承自 SCNView 的,所以其中关于 3D 物体的属性、方法都是 SCNView 的(如 SCNScene、SCNNode 等)。

下面简单介绍一下 ARKit 中各个类是如何协作的。

ARSCNView

最顶层的 ARSCNView 主要负责综合虚拟世界(SceneKit)的信息和现实世界的信息(由ARSession 类负责采集),然后将它们综合渲染呈现出一个 AR 世界。

ARSession

ARSession 类负责采集现实世界的信息。这一行为也被称作世界追踪。它主要的职责是:

  • 追踪设备的位置以及旋转,这里的两个信息均是相对于设备起始时的信息。
  • 追踪物理距离(以“米”为单位),例如 ARKit 检测到一个平面,我们希望知道这个平面有多大。
  • 追踪我们手动添加的希望追踪的点,例如我们手动添加的一个虚拟物体。

它采集到的现实世界信息以 ARFrame 的形式返回。

当然,为了有一个比较好的追踪效果,要满足以下要求:

  • 运动传感器不能停止工作。如果运动传感器停止了工作,那么就无法拿到设备的运动信息。根据我们之前提到的世界追踪的工作原理,毫无疑问,追踪质量会下降甚至无法工作。
  • 真实世界的场景需要有一定特征点可追踪。世界追踪需要不断分析和追踪捕捉到的图像序列中特征点,如果图像是一面白墙,那么特征点非常少,那么追踪质量就会下降。
  • 设备移动速度不能过快。如果设备移动太快,那么 ARKit 无法分析出不同图像帧之中的特征点的对应关系,也会导致追踪质量下降。

总的说来,就是要提示用户移动手机,且速度不能太快,要在略微复杂的场景中探测

ARFrame

ARFrame 包含了两部分信息:ARAnchor 和 ARCamera。其中,

  • ARCamera 指的是当前摄像机的位置和旋转信息。这一部分 ARKit 已经为我们配置好,不用特别配置。
  • ARAnchor 指的是现实世界中的锚点,具体解释如下:

ARAnchor

  • 可以把 ARAnchor(锚点)理解为真实世界中的某个点或平面,anchor 中包含位置信息和旋转信息。拿到 anchor 后,可以在该 anchor 处放置一些虚拟物体。
  • 与 SCNNode 可以绑定
  • 它有一个子类:ARPlaneAnchor,专门指的是一个代表水平面的锚点。

ARConfiguration

指的是 ARSession 将如何追踪世界,有以下几种子类:

  • ARWorldTrackingConfiguration(6 DOF)
    是 ARSession 的默认配置,以6个自由度(x y z轴上的位移及绕着三个轴的旋转)追踪现实锚点和虚拟物体。
  • AROrientationTrackingConfiguration(3 DOF)
    以 3 个自由度(没有位移,只有旋转)追踪,但是被苹果文档声明不推荐使用。这样的追踪质量将比较差。
  • ARFaceTrackingConfiguration(iPhone X Only)
    以面部识别来追踪,只有装备了 True Depth 前置景深摄像头的 iPhone X 才能使用。

而且,如果要开启平面检测,需要加入以下语句:

let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal

总结

用一段话总结的话,就是 ARSCNView 结合 SCNScene 中的虚拟世界信息和 ARsession 捕捉到的现实世界信息,渲染出 AR 世界。ARConfiguration 指导 ARSession 如何追踪世界,追踪的结果以 ARFrame 返回。ARFrame 中的 ANAnchor 信息为 SceneKit 中的 SCNNode 提供了一些放置的点,以便将虚拟节点和现实锚点绑定。

5. ARKit 中的 Delegate

ARSCNViewDelegate

先介绍 ARSCNView 的代理:ARSCNViewDelegate,他有以下几个回调方法。

func renderer(SCNSceneRenderer, nodeFor: ARAnchor)

当 ARSession 检测到一个锚点时,可以在这个回调方法中决定是否给它返回一个 SCNNode。默认是返回一个空的 SCNNode(),我们可以根据自己的需要将它改成只在检测到平面锚点(ARPlaneAnchor)时返回一个锚点,诸如此类。

func renderer(SCNSceneRenderer, didAdd: SCNNode, for: ARAnchor)
func renderer(SCNSceneRenderer, willUpdate: SCNNode, for: ARAnchor)
func renderer(SCNSceneRenderer, didUpdate: SCNNode, for: ARAnchor)
func renderer(SCNSceneRenderer, didRemove: SCNNode, for: ARAnchor)

以上方法会在为一个锚点已经添加、将要更新、已经更新、已经移除一个虚拟锚点时进行回调。

ARSessionDelegate

ARSession 类也有自己的代理:ARSessionDelegate

func session(ARSession, didUpdate: ARFrame)

在 ARKit 中,当用户移动手机时,会实时更新很多 ARFrame。这个方法会在更新了 ARFrame 时,进行回调。它可以用于类似于始终想维持一个虚拟物体在屏幕中间的场景,只需要在这个方法中将该节点的位置更新为最新的 ARFrame 的中心点即可。

func session(ARSession, didAdd: [ARAnchor])
func session(ARSession, didUpdate: [ARAnchor])
func session(ARSession, didRemove: [ARAnchor])

如果使用了 ARSCNViewDelegate 或 ARSKViewDelegate,那上面三个方法不必实现。因为另外的 Delegate 的方法中除了锚点以外,还包含节点信息,这可以让我们有更多的信息进行处理。

6. 一些实践

下面就几种常用场景给出一些示例代码。

添加物体

添加物体可以有以下两种方式:自动检测并添加或者手动点击添加。

自动检测并添加

主要利用了 ARSCNViewDelegate 中的回调方法:

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
    if let planeAnchor = anchor as? ARPlaneAnchor {
        let node = SCNNode()
        node.geometry = SCNBox(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.y), length: CGFloat(planeAnchor.extent.z), chamferRadius: 0)
        return node
    }
    return nil
}

这段代码的含义是:如果找到了一个平面锚点,那就返回一个和该平面锚点的长宽高分别相同的白色长方体节点。当你移动手机寻找平面时,一旦找到,便会有一个白色平面出现在屏幕上。

手动点击添加

ARKit 允许用户在画面中点击,来和虚拟世界互动。
比如我们之前添加了一个 UITapGestureRecognizer,selector 是如下方法:

@objc func didTap(_ sender: UITapGestureRecognizer) {
    let location = sender.location(in: sceneView)
    let hitResults = sceneView.hitTest(location, types: .featurePoint)
    if let result = hitResults.first {
        let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
        let boxNode = SCNNode(geometry: box)
        boxNode.position = SCNVector3(x: result.worldTransform.columns.3.x,
                                  y: result.worldTransform.columns.3.y,
                                  z: result.worldTransform.columns.3.z)
        sceneView.scene.rootNode.addChildNode(boxNode)
    }
}

这其中用到了一个 ARHitTestResult 类,它可以检测用户手指点击的地方有没有经过一些符合要求的点/面,有如下几种选项:

  • featurePoint
    返回当前图像中 Hit-testing 射线经过的 3D 特征点。
  • estimatedHorizontalPlane
    返回当前图像中 Hit-testing 射线经过的预估平面。
  • existingPlaneUsingExtent
    返回当前图像中 Hit-testing 射线经过的有大小范围的平面。
  • existingPlane
    返回当前图像中 Hit-testing 射线经过的无限大小的平面。

上面一段代码的含义是:首先记录用户点击的位置,然后判断有没有点击到特征点,并将结果按从近到远的顺序返回。如果有最近的一个结果,就生成一个长宽高都为0.1米的立方体,并把它放在那个特征点上。

其中将 ARHitTestResult 信息转换成三维坐标,用到了 result.worldTransform.columns.3.x(y,z)的信息。我们不深究其中原理,只需知道它的转换方法就可以了。

更新物体位置

这时可以使用 ARSessionDelegate 的代理方法:

func session(_ session: ARSession, didUpdate frame: ARFrame) {
    if boxNode != nil {
        let mat = frame.camera.transform.columns.3
        boxNode?.position = SCNVector3Make((mat.x) * 3, (mat.y) * 3, (mat.z) * 3 - 0.5)
    }
}

也就是当更新了一个 ARFrame,就把一个之前建立好的 SCNNode 的位置更新为 frame 的中心点。这里 * 3 是为了放大移动的效果。注意,这里也用到了上面所说的 worldTransform 和 SCNVector3 的转换方法。

7. ARGitHubCommits 思路

有了以上的知识基础,我们可以用以下思路来构建这个项目:

  1. 获取 GitHub 的 Commits 数据。
  2. 建立 ARSCNView,开始 ARSession。
  3. 提示用户移动手机,探测水平面。
  4. 探测成功后,在 ARSCNView 中的 SCNScene 中加入各个 SCNNode。

具体的代码,欢迎参考GitHub

8. 参考

下面是一些可以参考的文章/GitHub链接,仅供参考:

微博:@滑滑鸡

GitHub:songkuixi

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容