AR场景实践(1) - ARKit探索

1,479 阅读9分钟

AR技术简介

  • 增强现实技术(Augmented Reality,简称 AR),是一种实时地计算相机影像的位置及角度并加上相应内容(图像、视频、3D模型)的技术,其本质就是在设备屏幕上把虚拟世界套在现实世界并进行互动。
  • 当下移动互联网常见的AR的应用场景:
    • AR内容导览 - 扫描目标物体,浏览内容(建筑、设计、广告等)
    • AR互动 - 教育教学、游戏
    • AR试妆、试穿
  • 作为一个iOS客户端的开发,我们搭建一个基础的AR场景需要具备一下这些技术能力,或者说理解和应用能力
    • 相机/摄像头,原生开发能力
    • 3D立体模型(当下直接使用现成的)
    • 三维的矩阵坐标的理解,便于对物体的方位设定、移动等操作
    • ARKit、SceneKit框架API的理解和使用

ARKit

框架

主流的应用场景基本都是基于3D实现AR技术,也有基于2D实现的:

  • SceneKit:基于3D场景实现的增强现实
  • SpriktKit:基于2D场景实现的增强现实 其中SceneKit的出场率是极高的。为什么必须要依赖这两个框架呢?因为只有只有游戏引擎才能加载物体模型。
视图

ARKit提供两种虚拟增强现实的视图:

  • ARSCNView:展示3D效果
  • ARSKView:展示2D效果 这两个视图都会用相机视图作为背景视图,这个相机视图就是ARKit框架中的ARCamera捕捉而来的
ARSession

ARSession可以说是ARKit中非常重要的一个类,它是ARSCNView和ARCamera进行关联的桥梁,可以理解为会话,要运行一个ARSession会话,就必须指定一个会话追踪配置对象:ARConfiguration,其主要目的就是负责追踪相机在3D世界中的位置以及特征捕捉。

节点

SCNNode,节点是个抽象的概念,节是个看不见,摸不到的东西,没有几何形状,但是它有位置,以及自身坐标系。在场景中创建一个添加节点后,你就可以在这个节点上放游戏元素了,比如模型,灯光,摄像机等等... 节点上可以添加节点的,每个节点都有自身坐标系.

ARKit架构图

流程图 (2).jpg

场景实践:AR识图+AR模型展示

需求场景

我们设想一种场景,公司的产品(App)需要扫描带有公司文化图片的图片,识别成功后展示预设的3D模型动画,这可能是AR技术中最为简单的场景了。

实现第一步:识图

首先我们需要创建一个AR视图

.....
/// AR、3D框架
import SceneKit
import ARKit

/// 创建AR视图
private lazy var arSCNView: ARSCNView = {
    let scnview = ARSCNView(frame: self.view.bounds)
    return scnview
}()

创建会话配置,此处仅是demo验证,所以我们直接可以在ViewController生命周内创建它

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let arConfig = ARWorldTrackingConfiguration()
    arConfig.detectionImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil)
    arSCNView.session.run(arConfig, options: [.resetTracking, .removeExistingAnchors])
}

我们可以将要识别的图片内容先添加到工程内,我们创建一个Assets并将需要识别的图片添加进去,我们在这里添加了一个飞机的图片

image.png 添加图片的时候,我们需要注意,如果出现了如下警告:AR reference image must have non zero positive width,说明没有给图片添加物理尺寸。

企业微信截图_6cf51891-40ee-49e0-9e5a-35e2b4f4bef2.png 在使用ARKit添加图片,这是必须的,否则它无法判断图像在相机图像中显得小是因为它在物理上很小而且在附近,还是因为它很大而且很远。我们只需要添加上就好,添加完图片后我们需要再刚才的会话配置代码部分加上,要在场景中检测的图像的设置

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let arConfig = ARWorldTrackingConfiguration()
    // 要在场景中检测的图像的设置
    arConfig.detectionImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil)
    arSCNView.session.run(arConfig, options: [.resetTracking, .removeExistingAnchors])
}

在设置完会话配置后,我们需要给AR视图设置代理,并实现对应的代理方法

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(self.arSCNView)
    arSCNView.delegate = self
}

extension ARSCNViewController: ARSCNViewDelegate {
    /// 当新节点已映射到给定图像锚点时会调用。
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        let imageAnchor = ARImageAnchor.init(anchor: anchor)
        let referenceImage = imageAnchor.referenceImage
        if referenceImage.name == "plane" {
            print("sacn 成功")
        }
    }
}

此时,识别图的代码我们已经完成,我们run一下看看

相机扫描我们刚才添加的那张飞机图片,执行了ARSCNViewDelegate的代理方法,并且成功打印了“sacn 成功”,这说明,我们基本简单的识图逻辑已经完成,

实现第二步:添加模型

首先我们要准备一个模型,SceneKit支持的模型类型如下

这里我准备的是一个飞机模型,没有模型的可以在模型网站上下载,我们新增了一个scnassets,将飞机模型的.scn文件添加到目录下

企业微信截图_7a62a7ed-b09f-4544-8296-fbdf8990bb0c.png 在进行代码部分的新增调整前,我先介绍下SCNScene和SCNVector3,因为下面的代码我们会使用到它,SCNScene 是包含节点层次结构和全局属性的容器,并构成可一个显示的 3D 场景。

image.png 我们要使用 SceneKit 显示 3D 内容,我们就需要创建一个SCNScene实例,同样,当实例场景创建后,我们需要确定一个接收者的位置,这个位置是SCNVector3对象的值。

SCNVector3是一个三分向量的表示,我们可以简单理解为,我们这个接收者的位置在3D世界中的坐标信息。通过下面图,我们可以更好的理解

企业微信截图_55bd5367-8a5c-450a-a7b5-98839f55e93a.png 两个API我们已经基本清楚了,现在开始编码,我们新增一个方法,抽象新增场景节点的代码,方便识图部分的代码的调用

private func arViewShow() {
    // 加载scn文件,.scn的文件路径
    let scene = SCNScene.init(named: "thirdart.scnassets/plane.scn")
    
    // 目录下可以有多个文件,所以这里取我们要用的这个节点
    let planeNode = scene?.rootNode.childNodes.first
    
    // 接下来我们需要确定接收者的位置,
    planeNode?.position = SCNVector3(x: 0, y: -1, z: -1)
    
    // 将节点附加到接收者的 childNodes 数组。
    self.arSCNView.scene.rootNode.addChildNode(planeNode!)
}

此时,我们可以run起来看看效果,扫描飞机图标,识别成功,展示3D飞机模型 ezgif-2-8756606779 (1).gif

当然我们还可以给3D模型增加一些动画,这个飞机模型,本身是没有动画的,我们将使用Scene的另一个API:SCNAction,一个简单的、可重复使用的动画,可以更改我们附加到的任何节点的属性,下面 我们添加上,看看这些动画实现的效果

我们先添加一个旋转动画,效果如下图左

// 为了更好的效果,我们将原有的位置z值调整的更小,也就是,离我们更远,方便飞机旋转是的全景展示
planeNode?.position = SCNVector3(x: 0, y: -1, z: -5)
// 添加一个沿Y轴旋转的动画
// 这是一个以弧度为单位的相对值旋转节点的动作,这里我们设置了duration为1,y轴值为-5
planeNode?.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: -5, z: 0, duration: 1)))

我们再换一个缩放的动画,效果如下图中

// 添加一个缩放动画
// 这个是通过相对值更改节点的 x、y 和 z 比例值,达到缩放效果,我们设置该值为2,它应该在3s内变大两倍
planeNode?.runAction(SCNAction.scale(by: 2, duration: 3))

我们再换一个平移的动画,效果如下图右

// 为了更好的效果,我们将原有的位置z值调整的更小,也就是,离我们更远
planeNode?.position = SCNVector3(x: 0, y: -1, z: -5)
// 添加一个平移动画
// 这是一个将节点移动到新位置的动作,我们将新的位置z值调整为10,预测,这将是一个飞机由远及近,飞至我们身后的动画
planeNode?.runAction(SCNAction.move(by: SCNVector3Make(0, 0, 10), duration: 3))

效果展示:

ezgif-3-005875d2ae.gif

场景实践:AR识别物体

ARKit的物体检测功能可用于3D检测。如果进行物体检测,这个其实可以参考上面识图的逻辑。

现实中有一个物体,我们通过AR识别出这个物体,那我们需要本地有一个已知物体,通过AR实物检测来跟已知物体进行匹配,从而实现识别的能力

我们需要先实现一个物体检测的功能,苹果官方提供了检测识别的demo - Scanning and detecting 3D objects ,我们直接可以用,这里我们也直接用苹果的demo示例图来看下识别检测的过程:

image.png

如果对效果满意,就可以通过demo导入到Xcode中,以供检测识别。分享出的是一个 .arobject文件,我们将这个文件存放到ar resource group目录下,此时,我们可以着手代码部分了,当然代码部分相比图片识别,变化并不大,在场景中检测的对象需要设置为: ARReferenceObject

ARReferenceObject就是ARKit在物理环境中检测到的三维对象的描述,需要使用ARObjectScanningConfiguration运行AR会话,以启用高保真度空间映射数据的收集,这就是我们前面说的 苹果官方的物体检测demo 所做的事情。

// 同样,我们需要设置在场景中检测的识物的设置
let arConfig = ARWorldTrackingConfiguration()
arConfig.detectionObjects = ARReferenceObject.referenceObjects(inGroupNamed: "AR Resources", bundle: nil)!
arSCNView.session.run(arConfig)

同样设置代理

arSCNView.delegate = self

接下来看代理实现部分

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    let bodyAnchor = ARObjectAnchor.init(anchor: anchor)
    let referenceObject = bodyAnchor.referenceObject
    if referenceObject.name == "scanbody" {
        print("sacn 成功")
    }
}

从上面的代码可以看到,抛开获取 .arobject 的部分,检测图片和检测实物的实现高度相似,这里必要要说明的是,不是所有物体都可以通过ARKit进行检测的,对于实物有基本的一些要求:

  • 太大或者太小的物品
  • 刚性物体,不会被轻易挤压变形
  • 纹理细节丰富,不要单纯的平面
  • 无表面反射、无透明效果

以这个为要求为基础,我们尝试进行AR的物体检测,看看能否进行物体扫描识别,因为我们并没有苹果demo示例中的那个狐狸玩偶,这里我随手拿了一瓶饮料来验证,看如下效果:

image.png e7fa8f1a-957e-4c3c-90c4-17b1284d7f87.gif

总结

本章内容覆盖了ARKit框架的一些介绍,也以demo的方式实践了AR视图、AR模型展示、模型动画。AR技术的应用场景在个别领域里是非常丰富的,作为App的开发者,对于ARKit框架的使用挖掘也是非常重要的。后面我们还将继续结合一些实际使用场景探索ARKit框架的内容以及相关的AR技术。