ARKit3 和 RealityKit 介绍

介绍ARKit3和RealityKit带来了哪些功能,有哪些屌爆的特性以及体验

2020-01-15

TOC

前言

在之前的苹果系统上进行AR开发需要依赖于SceneKit(一个为手机端开发3D游戏或者3D内容而研发出来的3D渲染库),苹果基于这个库封装出来一些AR相关功能供开发者使用,但是SceneKit不是专门的AR库,带来了很多问题,并且提供的功能和实际表现差强人意。

2019年苹果推出了一个新的ARKit版本(ARKit3)和专门为AR场景研发的RealityKit。从WWDC视频和官网介绍上可以看出新的ARKit3 + RealityKit组合带来了很多新的应用场景以及显著提高了开发体验和使用体验。

所有这些功能基本上都需要最新系统(iOS13)和最新设备(XR及以上),语言上需要使用Swift进行开发。下面我们挑一些重点项一个个研究一下。

Reality Composer & RealityKit

这个全新的高级框架专门为AR量身定制,能够提供逼真的图像渲染、相机特效、动画、物理特效等等。借助原生ARKit整合提供物理的超逼真渲染、变换和骨骼动画、空间音频和刚体物理等等。

听起来想自己实现这些门槛会非常高,需要非常资深的数学知识和物理知识才能实现。但是使用Reality Composer只需拖拖拖点点点,在几乎没几行代码的情况下就能构建出一个体验非常良好的AR APP。

Reality Composer

Reality Composer在MAC、iPhone、iPad三个平台都可以使用,同时可以进行无缝切换。它内建AR资源库,也可以自己导入一些新的第三方资源。常规的动画或者物理特性已经完整内嵌在工具里,只需选择想使用的动画或者动作,比如旋转或者摆动等等。不管你是开发者还是产品还是设计师都可以无门槛的创造属于你自己的AR场景。

Composer的界面非常的简洁,只有零星几个按钮可以触发,比如添加对象、添加文本、添加行为、修改属性等等。但是简洁的界面背后藏不住这个工具的强悍,整个使用过程非常“傻瓜式”,不需要进行太多思考就可知道应该如何使用,下面贴几张图感受一下。

添加触发器

触发器类型如下:

  • Start - 当AR场景开始时触发。
  • Tap - 当点击时触发。
  • Proximity - 当相机识别到靠近行为时触发。
  • Collision - 当碰撞时触发。
  • Notification - 当接收到通知时触发。

添加动作

可以看到动作是一个一个按顺序执行的,如果想要同时执行多个动作可以组成一个group(拖拖拖就能完成),如果想循环也可以很方便的一键添加。

还可以添加各类动作,比如突出、旋转、轨道旋转、自定义等等。

当编辑完AR场景以后可以一键导出后缀为 .rcproject 的文件,此文件可以直接拖入到XCODE里面。然后XCODE会基于Swift特性自动创建出对应于此资源文件的Class供你使用。以官方DEMO上的 Experience.rcproject 为例,它会生成一个 Experience.swift 类,里面完整的包含了你在Composer里面定义的所有行为,包括load方式。然后就可以在工程里这样使用:

Experience.loadGameAsync { [weak self] result in
    switch result {
    case .success(let game):
        // success                        
    case .failure(let error):
        // error
    }
}

如果看到这些还觉得不够震撼的话,建议自己下下来DEMO跑到真机上体验一下,你会发现实现一个不算简单的小游戏,真正动手写的代码寥寥几行。地址在这里,Creating a Game with Reality Composer

RealityKit

这里稍微的展示一下RealityKit的强悍和简便,带大家演示一下如何把一个虚拟对象加入到AR场景中。

  1. 创建 ARView

    let view = ARView(frame: self.view.bounds)
    // 添加ARCoachingOverlayView
    view.addCoaching(goal: .horizontalPlane, delegate: self)
    
  2. 添加 ARCoachingOverlayView 。顾名思义,这个视图是引导用户去寻找合适的AR场景,帮助 ARView 搜集所需的信息,整个过程都进行了良好的封装,全部自动识别和判定。添加代码大概如下:

    func addCoaching(goal: ARCoachingOverlayView.Goal, delegate: ARCoachingOverlayViewDelegate?) {
        // Create a ARCoachingOverlayView object
        let coachingOverlay = ARCoachingOverlayView()
        coachingOverlay.translatesAutoresizingMaskIntoConstraints = false
        addSubview(coachingOverlay)
        NSLayoutConstraint.activate([
            coachingOverlay.centerXAnchor.constraint(equalTo: self.centerXAnchor),
            coachingOverlay.centerYAnchor.constraint(equalTo: self.centerYAnchor),
            coachingOverlay.widthAnchor.constraint(equalTo: self.widthAnchor),
            coachingOverlay.heightAnchor.constraint(equalTo: self.heightAnchor)
        ])
        
        // Set the Augmented Reality goal
        coachingOverlay.goal = goal
        // Set the ARSession
        coachingOverlay.session = self.session
        // Set the delegate for any callbacks
        coachingOverlay.delegate = delegate
        coachingOverlay.activatesAutomatically = true
    }
    
  3. 启动AR会话。

    arView.session.run(ARWorldTrackingConfiguration(), options: [])
    
  4. 添加虚拟对象到ARView

    loadRequest = ModelEntity.loadAsync(named: "gramophone")
        .sink(receiveCompletion: { [weak self] (loadCompletion) in
                // 如果出错会跑到这里
            }, receiveValue: { [weak self] (entity) in
                // 如果成功会跑到这里
                guard let self = self else { return }
                // Place model on a horizontal plane.
                let anchor = AnchorEntity(plane: .horizontal, minimumBounds: [0.15, 0.15])
                self.arView.scene.anchors.append(anchor)
        
                entity.scale = [1, 1, 1] * 0.006
                anchor.children.append(entity)
        })
    

    这里 loadAsync() 是异步加载的方式,不会造成卡顿现象。另外还提供一种同步加载方法load(),会造成明显的卡顿,想不通什么场景下会用到这个方法。

    这里注意的是loadAsync返回的是 LoadRequest 类型, LoadRequest 实现了Publisher协议,所以看到下方是使用 sink 方法进行订阅接收load结果,使用起来非常简便。

    还有一个注意的点是可以看到我这里是使用 loadRequest 对象对 sink 的返回体进行持有,这样才能保证整个load流程不会被中断,因为是个异步过程,如果不进行持有的话在结果返回前就可能被释放了,然后 receiveValue 这块代码永远都没机会执行。

People Occlusion(人物遮挡)

在之前的AR环境(ARKit2)里绘制虚拟对象只能显示在最顶层,也就是虚拟对象会覆盖所有其他内容。比如一个人经过一个虚拟对象并产生遮住行为时虚拟内容会显示在人体之上。产生这个问题的原因很简单,AR的绘制可以简单理解为把虚拟内容绘制在相机捕获出来的平面内容上,比如下图所示:

虚拟对象在人体之前

最新的ARKit可以从相机内容中分割出人体部分,然后把人体和虚拟对象结合在一起进行绘制就能达到人体遮照效果,如下图所示:

虚拟对象在人体之后

进一步的在XR及以上的设备上可以利用景深(Depth)功能探测出多个人体和虚拟对象距离相机有多远,从而灵活的判断哪一部分需要遮照,哪一部分不需要,如下图所示:

虚拟对象在两个人体之间

开启和关闭People Occlusion效果的代码非常简单,只需修改config的属性即可,如下:

if true/false {
    config.frameSemantics.remove(.personSegmentationWithDepth)
} else {
    config.frameSemantics.insert(.personSegmentationWithDepth)
}
arView.session.run(config)

也可以到苹果官网进行DEMO的下载,自己进行尝试,代码地址

针对人物遮挡有很细致的原理和高阶用法介绍(获取到People Occlusion的结果自行绘制)有专门的WWDC视频,有兴趣的可以去看一下,Bringing People into AR的前半部分。

Motion Capture(动作捕捉)

用单个摄像头实时捕捉人物的动作。将身体姿态和动作化为一系列关节及骨骼活动,可以让虚拟对象(机器人)跟着画面中的人物一起动。

识别骨骼

依赖于机器学习,现在可以在相机画面中识别出人体骨骼结构,分为3D和2D两种类型,以3D为例参考如下图:

3D骨骼结构

可以看出从最左侧的人像里面识别出了三维的骨骼结构,骨骼结构包含着数十个节点,以根节点为基础连通了所有其他子节点,如下图所示:

3D骨骼结构细节

连通角色(Character)和机器人

动作捕捉的硬性要求就是这些虚拟对象创建时也需要有同样的骨骼结构及同样的命名方式,所以带来了画面中的人物角色跟虚拟对象连通的可能性,连通方式也非常简单,分为下面几个步骤:

  1. 使用 ARBodyTrackingConfiguration 启动AR场景
  2. 添加 AnchorEntity
  3. 加载 BodyTrackedEntity 添加到上一步的 AnchorEntity 里面

监听骨骼节点的变化

当上面连通部分完成以后就可以在 ARSessionDelegate 里面获取到骨骼节点在每一帧的变化信息,然后可以把变化赋值到 AnchorEntity 同步进行改变。可以看到实现一个基础的动作捕捉功能非常简单,只需寥寥几行代码就可实现。

当然,这只是一个基础用法,当你能获取到人物的每一帧的骨骼节点变化信息时,能扩展出很多应用场景,就看你的想象力有多丰富了。针对骨骼节点是如何组装的、如何表现的以及3D和2D模式下有何不同有着更细节的WWDC视频,有兴趣的可以去了解一下, Bringing People into AR的后半部分。

Simultaneous Front and Back Camera(同时使用前置和后置摄像头)

现在,您可以同时使用前置和后置摄像头来进行面部和现实场景跟踪,开创新的可能。例如,用户可以使用自己的面部与后置摄像头视图中的 AR 内容进行交互。

直白的讲就是现在可以在使用后置摄像头跟踪现实场景变化的同时还可以使用前置摄像头捕获面部的变化,反之亦然,就像下面这样设置就可以:

let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizontal, .vertical]

if ARWorldTrackingConfiguration.supportsUserFaceTracking {
    configuration.userFaceTrackingEnabled = true
}

// 反过来也可以这样设置
let configuration = ARFaceTrackingConfiguration()
if ARFaceTrackingConfiguration.supportsWorldTracking {
    configuration.isWorldTrackingEnabled = true
}

苹果给出的一个简单的应用场景示例图,在实时场景画面中绘制面部数据的改变:

在实时场景画面中绘制面部数据的改变

但是发现这个特性目前还没有办法很好的使用在 ARKit + RealityKit 这个组合上,搜寻了所有的官方文档以及资料都是停留在 ARKit + SceneKit 的例子上。我还发起了一个提问,得到的答复也是如此,StackOverflow提问地址

如果对旧的实现方式感兴趣,可以参考这个官方文档和例子,Tracking and Visualizing Faces

Collaborative Sessions(协作会话)

通过多人之间的实时协作会话,您可以构建一个协作的现实场景地图,加快 AR 体验的开发速度,用户也能更快地获得像多人游戏那样的共通 AR 体验。

通过协作会话,ARKit 定期地给每一个在会话里的人提供数据,然后你可以选择一种network协议来发送这个数据。会话提供的数据里面包含着每一个参与者的锚点信息,这些锚点代表着每一个参与者的大概位置,ARKit 还提供其他参与者所创造出来的所有锚点。如下图是苹果官方给出的一个DEMO里的示例图:

多个用户之间的协作会话

实现协作会话的过程大致如下:

开启协作模式

协作模式只限在 ARWorldTrackingConfiguration 场景下。可以使用 isCollaborationEnabled 变量开启协作模式。

configuration = ARWorldTrackingConfiguration()

// Enable a collaborative session.
configuration?.isCollaborationEnabled = true

// Enable realistic reflections.
configuration?.environmentTexturing = .automatic

// Begin the session.
arView.session.run(configuration!)

收集协作数据,并发送到其他用户

当开启协作模式时 ARKit 会定期地调用 session(_:didOutputCollaborationData:) 方法,监听到此代理方法被调用时你需要把这些数据通过网络协议发送给其他用户,以保证所有用户的数据都是一致的。可用的一种发送方式是使用MultipeerConnectivity,这是苹果官方提供的一种多点连接通信Framework。

更新其他用户发送过来的数据

当监听到 session(_:didReceive:fromPeer:) 代理方法被调用时你需要及时更新到 ARView 的当前会话里,如下:

func receivedData(_ data: Data, from peer: MCPeerID) {
    if let collaborationData = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ARSession.CollaborationData.self, from: data) {
        arView.session.update(with: collaborationData)
        return
    }
    // ...
}

更快的促成场景地图的合并

ARKit 需要通过两个用户扫描出来的各自的场景地图信息中判断用户之间的对应关系,然后进行合并操作。当 ARKit 成功的进行场景合并操作以后,它就可以开始分享每个用户的对应位置以及他们创建出来的新的锚点。

如果想要促成场景合并,一个用户必须扫描到前一个用户访问过的某个区域,也就是需要产生重叠区域,然后 ARKit 会默默给你促成合并,参考以下信息:

如图两个用户各自在不同地方收集了不同的场景信息,分别为绿色点和粉色点。

场景地图合并之前

当两个用户拍摄到同一个区域时 ARKit 能计算出两个用户相对位置和信息对应关系,然后默默进行合并,如下图合并以后都变成了黄色的点,再往后用户各自收集的信息都能实时分享到另一个用户那里。

场景地图合并之后

这样一个简单的协作会话流程就已经走通了,剩下的就是看你如何补充更多细节了,比如点击屏幕时根据换算出的位置填充虚拟对象进去,同时也可以给每个用户标配一种颜色用来区别不同用户摆放的虚拟对象,还可以给虚拟对象添加各种交互事件等等。

这一部分简单的实现DEMO可以参考苹果的这个文档,Creating a Collaborative Session。 还有更细致的WWDC视频也可以看一下,Building Collaborative AR Experiences

结语

以上就是最新的 ARKit3RealityKit 所带来的部分介绍,除了这些还有很多优化型的更新,比如一次检测最多 100 张图像,并自动估计图像的实际尺寸,3D对象检测功能变得更强大,可以在复杂的环境中更好地识别对象,可以更快地检测环境中的平面等等等等。

有一点需要提的是目前阶段还没有办法使用 RealityKit 完全替代 SceneKit。毕竟目前的 RealityKit 只是第一个版本,有一些功能点还无法做到 SceneKit 那么丰富,所以一部分功能的官方文档和例子上都停留在去年或者前年的时间点上。还有一个点是可以看出上面整理的例子以及目前市面上的资料来说这些新功能点的实现都只停留在接入层面上,如果想做某一个功能点的深入实现,难度还是非常大的,需要你在这全新的领域里面不断开荒。

总而言之,苹果在19年针对AR功能进行了质的改进,尽可能的降低了接入门槛(Reality Composer)以及简化并提升了开发体验(RealityKit)。单单从提供一个全新的AR库的态度来说已经说明了苹果的决心,相信接下来随着每一年的更新迭代会让AR相关领域的开发变的更加丰富起来。