简单使用 ARKit

概览

什么是 AR?它不同于 VR,VR 是一种沉浸式体验,你眼前的一切都是虚拟的,根本不需要理会现实世界(当然,如果体验 VR 的地方很小,你可能还需要注意一下走位,免得发生意外),VR 是 Virtual Reality,也就是虚拟现实,而 AR 是 Augmented Reality,增强现实。AR 是对现实世界的一种补充,一种增强,你的大部分精力还是放在现实世界中。AR 被用来丰富化现实世界的图像,例如在一张什么都没有的桌子上显示出一个城堡,又或者在一面干干净净的墙面上绘制涂鸦或摆放一面电视,但不同于普通的 PS 照片,AR 可以是动态的,通过 AR 引擎创造出来的虚拟物体会根据人物的移动而自动调整位置,也就是说你从桌子的这个角走到另一个角,城堡依然在桌子上,位置不变,角度不变,甚至光照也不变,就好似真的有一座城堡在桌子上。

AR 和 VR 都是创造虚拟图像的技术,但前者是基于现实世界的,没有现实世界的图像,AR 创造出来的图像是没什么意义的,而 VR 是完全虚拟的,它会在虚拟世界中虚拟出一面桌子,然后在虚拟的桌子上在创造一个虚拟的城堡。

对于 AR 这样一个神奇的技术,Apple 在 WWDC 2017 上顺势推出了 AR 引擎和开发框架,这个框架就叫做 ARKit

ARKit

Apple 的 ARKit 可以将你创造的 2D 平面对象和 3D 立体对象,通过 iOS 设备的摄像头映射到现实世界中,透过屏幕你就能在当前周围的环境里看到你创造的对象 。

你可以通过 Apple 的 Metal、SpriteKit 和 SceneKit 来创造你的精灵,你也可以通过强大的第三方引擎例如 Unity、Unreal Engine 为你的 AR app 创造模型。

ARKit 基于 Visual Inertial Odometry (VIO) 技术实现,利用 iPhone 和 iPad 的各种传感器提供的数据来计算你的当前位置,同时更新虚拟物体的相对位置,还可以寻找到相对水平的平面,例如桌子。所以这需要庞大的计算力,Apple 为此也做了一定限制:

  • 如果要使用 worldTeacking,你的设备所使用的处理器必须大于 A9 处理器

下面就让我们一起来体验一下这神奇的框架吧!

使用 ARKit

在一切开始前,你需要打开最新的 Xcode 9.0 beta 版,然后新建一个工程:

1.新建一个 Augmented Reality App

2.填写 App 的 Bundle 名,在 Content Technology 一栏选择 SceneKit,这表示我们将使用 SceneKit 来创造 AR 环境

3.选择保存位置,一个 AR 工程就新建完毕了

新建好以后,你会看到工程里已经有一些文件,而且和以往一样,你可以直接运行这个工程到你的设备上,按下 Command + R 运行在 iPhone 上试试吧!

在调试 AR app 的时候,你可能需要到处走动,牵着一根线可能会大大阻碍你的行动,幸运的是 Xcode 9.0 开始支持了无线开发,你可以以无线的方式将 App 安装在设备上,并且还能正常接收调试数据,具体怎么做请看我的探索 Xcode 9

Demo 工程是让一艘飞船悬停在空中,如果你已经运行过这个 Demo 了应该会觉得效果还蛮不错,虽然某些 AR 技术的通病 Apple 还没有解决,让我们先来看一看这个工程里相对重要的文件有哪些吧:

  • art.scnassets:这里面存放的就是场景文件,一个场景里可以有多个物体

  • ship.scn:我们的飞船就来自这个场景文件,你可以打开并编辑它

  • texture.png:这是一个纹理球文件,每一个 3D 模型都应该需要纹理,否则看上去会很单调,除非你刻意创造那种场景。而这个纹理就是这艘飞船的纹理。

  • ViewController.swift:默认的视图控制器,它继承于 UIViewController 并遵守 ARSCNViewDelegate 协议。

  • Main.storyboard:Storyboard 文件,里面有一些默认的组件已经被添加好了

剩下的一些资源文件和普通的 Cocoa touch 工程没什么区别,相信大家应该都了解了。

那么让我们继续来看大家最关心的部分,ViewController 这个文件展示了显示一个虚拟物体在现实世界中的最基本的步骤和内容,相当有指导意义,我们以方法和属性区分,分别解释。

默认的 ViewController

@IBOutlet var sceneView: ARSCNView!

这就是 Xcode 帮我们创建好的一个 ARSceneView 对象,而且做好了牵线工作。这个 Scene 和普通的 SceneKit 的 Scene 不同的是,它是 AR Scene,专门显示 AR 的

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set the view's delegate 设置 view 的代理
        sceneView.delegate = self

        // Show statistics such as fps and timing information 显示调试信息
        sceneView.showsStatistics = true

        // Create a new scene 创造一个新的场景
        let scene = SCNScene(named: "art.scnassets/ship.scn")!

        // Set the scene to the view 使默认场景为我们新创建的场景
        sceneView.scene = scene
    }

在 viewDidLoad 中,我们可以为创建好的 sceneView 设置代理,showStatistics 属性可以指定是否显示调试信息,这些信息包含 fps 值、时间信息等。

我们之前在资源文件里看到了,有一个飞船的场景文件,这个场景文件也是在这里被载入的,使用 SCNScene 的初始化方法 init(named:) 来载入一个场景文件,最后我们将 sceneView 的默认场景替换成我们载入的场景。

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // Create a session configuration
        let configuration = ARWorldTrackingSessionConfiguration()

        // Run the view's session
        sceneView.session.run(configuration)
    }

每一个 AR 场景被创建时,它都需要一个配置类来告诉它应该使用怎样的方式去运行,配置类一共有两种:

  • ARWorldTrackingSessionConfiguration

  • ARSessionConfiguration

这里使用的就是第一种,两者的区别在于物体的摆放方式:

  • 设置为 ARWorldTrackingSessionConfiguration 时,物体放在基于现实世界的坐标系,你的摄像头只是这个坐标系的另一个点,移动设备的时候物体是相对坐标系静止的。
ARWorldTrackingSessionConfiguration
  • 设置为 ARSessionConfiguration 时,坐标系以你的设备为基准,你的设备平移时,整个坐标系也跟着平移,坐标系上的物体也跟着平移,所以摄像头和物体是相对静止的,只有设备横向旋转时能观察坐标系的其他部分的元素。
ARSessionConfiguration

在创建好配置类的对象后,你就可以使用 sceneView 的 session 来启动一次 AR session,这里的 session 就是每一个 AR 进程所需要的 session 对话,你可以使用 run() 方法来启动一个 session。

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // Pause the view's session
        sceneView.session.pause()
    }

当你不希望 session 继续占用系统资源时,你可以使用 pause() 来停止一次 session,你可以再一次使用 run 来启动新的 session。

另外,你还可以在 session 执行的过程中动态更新配置类。

    // Override to create and configure nodes for anchors added to the view's session.

    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        let node = SCNNode()
        return node
    }

    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {

    }

    func session(_ session: ARSession, didFailWithError error: Error) {
        // Present an error message to the user
    }

    func sessionWasInterrupted(_ session: ARSession) {
        // Inform the user that the session has been interrupted, for example, by presenting an overlay
    }

    func sessionInterruptionEnded(_ session: ARSession) {
        // Reset tracking and/or remove existing anchors if consistent tracking is required
    }

这里就列出了一些 ARSCNViewDelegate 中的代理方法,其中:

  • renderer(:nodeFor:):新建一个 Anchor(锚点)时会调用

  • renderer(:updateAtTime:):更新场景时会调用

  • session(:didFailWithError:):出现错误时会调用,此时会终止 session

  • sessionWasInterrupted(:):中断 session 时会调用

  • sessionInterruptionEnded(:):中断结束时会调用

到这里大家都应该知道一个场景的基本构造,以及如何载入一个场景。

添加一张“照片”到 AR 场景中

大家应该都发现了,我们是利用 SceneKit 创建的 AR 场景,所以我们可以将一部分 SceneKit 的东西拿来用,使用 SceneKit 创造的三维模型将其载入到 AR 世界里去,事实上,我们的 scn 场景文件本身也就是 SceneKit 的概念。
在这里我们将利用 SCNPlane 类来创造一个平面,然后设置这个平面的纹理为一张照片,在我们点击屏幕时将这个对象添加到 AR 场景中。

1.在 touchesBegan 中实现
既然是点击屏幕添加物体,那么就可以在 touchesBegan 中完成

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    // code here
}

2.获取当前帧
对于 AR 场景,当前帧不仅仅包含了帧信息和图像信息,还包含了当前 ARCamera 的坐标信息、sceneView 的大小等等,我们要实现在屏幕前添加一个物体,那么就得获取当前 Camera 的位置,否则我们添加的物体可能在任何一个地方,甚至你找不到的地方

// 从 session 中获取 currentFrame
guard let currentFrame = sceneView.session.currentFrame else { return }

3.创建一个 Node
要创建一个 Node,你需要一个继承自 SCNGeometry 的对象,这里我们使用的 SCNPlane 就继承自 SCNGeometry

// 创建一个 plane。并设置大小,这里的大小单位接近米,不能太大,否则最后会挡住我们的视线,看不出效果
let plane = SCNPlane(width: 0.6, height: 0.4)

// Meterial 就是我们的材料,也就是纹理,我们设置纹理的内容为一个 UIImage 对象
plane.firstMaterial?.diffuse.contents = UIImage(named: "material.jpg")
plane.firstMaterial?.lightingModel = .constant

// 利用创建好的 plane 来创建一个精灵(Node)
let planeNode = SCNNode(geometry: plane)

// 最后把精灵加入到 sceneView 的根节点
sceneView.scene.rootNode.addChildNode(planeNode)

每一个 scene 都有一个根节点,它类似于一个 scene 的坐标中心,所有子节点都依附于根节点,根节点产生的位移、旋转变化都会应用于子节点。

4.设置新节点的位置
我们说,需要把新加入的物体放在我们面前,那就必须要指定好它的详细位置,这时候我们最开始获取的 currentFrame 就有用了

// 熟悉 SceneKit 的朋友应该都了解这里的操作,这里的重点是利用 currentFrame 的 camera 属性来获取 transform
// 这个 transform 的各种位置属性就是当前摄像机所处的位置信息,所以我们可以直接利用它
var translation = matrix_identity_float4x4
translation.columns.3.z = -20
let transform = matrix_multiply(currentFrame.camera.transform, translation)

// 最后把 transform 传给 node 的 simdTransform
planeNode.simdTransform = transform

5.运行,测试
现在重新编译工程,并运行在设备上,每当我们点击一下屏幕时,面前都会有一张我们的照片出现,而且此时你可以移动相机,在其他地方继续添加照片,最后可以创造出一个很帅气的场景~

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

推荐阅读更多精彩内容

  • ARKit ARKit框架通过集成iOS设备摄像头和运动功能,在您的应用程序或游戏中产生增强现实体验。 概述 增强...
    暗夜夜夜行路阅读 5,764评论 0 17
  • 一、AR简介: 增强现实技术(Augmented Reality,简称 AR),是一种实时地计算摄影机影像的位置及...
    rectinajh阅读 2,048评论 0 6
  • 一、AR简介: 多媒体捕捉现实图像:如摄像头 三维建模:3D立体模型 传感器追踪:主要追踪现实世界动态物体的六轴变...
    DeerRun阅读 1,393评论 0 2
  • 大名鼎鼎的松下公司创始人松下幸之助曾提出:“松下只招70分水平的人,不招90分优秀的人才”。他的主要理由在于,越优...
    彭荣模HR视野阅读 561评论 0 0
  • 敬畏之心——夫子家 我出生在河边,从小就听老人们常说一句话:在河里淹死的,大多是会水的人。 所以,我从小就有些怕水...
    夫子家阅读 232评论 1 2