我正在参加「启航计划」

前语

6月21日苹果发布了Xcode 15 beta 2,包含了visionOS 1 beta,在正式进入visionOS的开发前,咱们首先学习一下苹果与AR相关的几个原生结构的运用以及其间的核心概念,最后编写一个简略的ARDemo将涉及的结构串起来。

Before visionOS: iOS AR场景下实现手指识别交互-ARKit + RealityKit + VisionKit

先睹为快:Demo 演示 (ARKit识别平面,RealityKit增加水平面、前左右三面墙、彩球,发射白球,处理磕碰检测,VisionKit检测手指发射白球)

AR

AR概述

AR是英文Augmented Reality的缩写,意为增强实际,而VR Virtual Reality为虚拟实际。AR着重的是对实在国际的增强,而VR是脱离了实在国际发明的虚拟国际,微软提出的MR(Mixed Reality)混合实际,是交融实在和虚拟国际的技能,着重物理实体和数字目标共存并实时相互作用,如虚实遮挡等。所以说Apple Vision Pro是一款MR设备,而非纯AR或许VR设备。

AR技能的根底是对实在环境的定位与盯梢,然后进行3D重建以完结虚拟目标与实在物体的交融、遮挡及磕碰等仿真处理。而定位与盯梢技能首要依靠于视觉惯性里程计和惯性导航体系,直白地讲首要依靠陀螺仪、加速度计以及其他图像处理相关的元器件和cv算法,两者并行作用、相互校验互补。

可是相机感光元器件和加速度计、陀螺仪都是对温度灵敏的元器件,这也便是为什么初代ARKit下的AR运用中长时刻运用后会出现模型漂移的现象。而图像算法无法从光滑、无纹路、反光的表面提取特征值,然后无法构建对环境的了解和检测识别平面。为处理这一问题,苹果在iphone 12 pro以上pro设备装备了LiDAR传感器(Light identification Detection And Ranging激光探测与测距),不受弱纹路的影响,可以实时精准高效地重建场景几许。

深度相机的引入尽管可以加深对环境的了解,可是因为来自太阳光的红外散射会过滤掉深度相机中的红外线,所以在户外并不能正常运转,我想这也是Vision Pro呈现的都是室内的运用场景的原因之一。

ARKit

ARKit发布于2017年WWDC,支撑最低版别为iOS 11,设备为iphone6s(A9处理器)及以上设备。ARKit供给的功用整体可以分为三个部分:运动盯梢、场景了解、烘托。

运动盯梢、场景了解上咱们无需关注细节,在API层面ARKit中的ARSession给咱们供给了打开摄像头去检测、盯梢当前环境(空间、平面、人脸、图片、物体)的能力。

严格来讲ARKit并没有烘托的能力,在ARKit 1版别到RealityKit推出之前,AR的烘托需求依靠其他第三方烘托结构,如3D SceneKit、2D SpirteKit、Metal或许自界说烘托器,这得益于ARKit供给连续的摄像头图像流,可以方便的对接其他烘托。

Before visionOS: iOS AR场景下实现手指识别交互-ARKit + RealityKit + VisionKit

追溯SceneKit的源头,它是苹果开始在mac上用于3D烘托的结构,其初衷并不是为了AR而规划,或许说3D烘托并不能满足AR的烘托,要做到对实际的增强而不产生分裂感,需求支撑PBR、运动模糊、环境光估计、环境反射等,所以苹果在2019年发布了为AR烘托量身打造的RealtyKit结构,要点处理实际环境中的虚拟元素PBR烘托以及精准的行为模拟。然后构成了ARAPP = ARKit供给环境数据 + RealityKit烘托的新局面。

Before visionOS: iOS AR场景下实现手指识别交互-ARKit + RealityKit + VisionKit

RealityKit

RealityKit 简介

RealityKit发布于2019年,只支撑iOS 13以上体系,而iOS13一起也是支撑SwiftUI的起点,所以苹果推荐的运用方式便是RealityKit + SwiftUI的方式,而visionOS同样是需求与SwiftUI的界面建立合作。(也意味着开发者需求去运用swift、swiftUI、以及iOS13以上新的API了,还在适配iOS13以下体系的小伙伴,是时候跟相关负责人谈一谈了。)

Before visionOS: iOS AR场景下实现手指识别交互-ARKit + RealityKit + VisionKit

RealityKit包含四类实体:ARView、Scene、Anchor、Entity,AR运用中的一切虚拟元素都是以实体的方式存在,其间ARView是进入AR国际的进口,在创立ARView时会自动创立一个Scene,而Scene是放置一切实体目标的容器,实体的增加需求指定在空间中的锚点,即AnchorEntity,用于在实际空间与虚拟之间建立相关联系,在增加AnchorEntity后再将实体目标增加到AnchorEntity构成层级联系。

Before visionOS: iOS AR场景下实现手指识别交互-ARKit + RealityKit + VisionKit

RealityKit的亮点

RealityKit的发布时刻从AR的布局来看相对较晚或许说较新,新的结构苹果一般都加入了很多现代性的规划,如支撑Combine、实体组件形式等。

  • 实体组件体系ESC:RealityKit引入了完好的实体组件体系(Entity Component System),即插即用的组件风格极大地避免了多层承继和彻底杜绝了多承继带来的维护问题。Entity本身不具有外观和功用,经过挂载不同的组件而构成不同的表现和行为特性,如TransformComponent组件包含了虚拟元素在实际空间中的方位、缩放等信息。CollisionComponent则界说了虚拟元素与其他物体发生磕碰的细节。

Entity默许挂载了TransformComponent和SynchronizationComponent(用于不同设备之间同步数据),AnchorEntity默许还挂载了AnchoringComponent所以才可以增加到Scene的anchors中。

Before visionOS: iOS AR场景下实现手指识别交互-ARKit + RealityKit + VisionKit

  • 网络同步:RealityKit彻底整合了Multipeer Connectivity近距离通讯结构,对AR运用供给内建的数据同步支撑,使实际国际数据信息与整个虚拟元素信息可以在一切参与方之间实时共享。

  • 支撑Combine的事情体系:同样是在2019年,苹果推出了声明性异步时刻处理结构Combine,运用它可以集成事情处理代码并消除嵌套闭包和基于约定的回调。其核心为Publisher(发布者)、Operator(操作符)、Subscriber(订阅者)。RealityKit供给了对场景状态、动画播映、音频播映、磕碰事情、网络同步等改变事情的订阅支撑。

VisionKit

有了ARKit的和RealityKit就已经可以开发完好的AR运用了,这儿介绍VisionKit是因为AR场景下的手势交互可以运用VisionKit结构来完结。

VisionKit是苹果的计算机视觉结构,包含了文字识别、手指关节点检测、人体关节点检测等功用。遗憾的是内部高度封装,开发者可以调用的API不多,这儿简略解说一下VisionKit的三要素:

  • Request:界说恳求特征来恳求结构检测内容。
  • Handler:恳求完结履行或处理恳求之后履行的回调。
  • Observation:取得潜在的结果或调查结果,这些调查基于恳求的VNObservation的实例。

Demo核心代码

ARKit + RealityKit

ARKit检测水平面:

    let config = ARWorldTrackingConfiguration()
  config.planeDetection = .horizontal
  // 启用环境纹路贴图和人形遮挡
  config.environmentTexturing = .automatic
    arView.session.delegate = self
  coachingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    coachingOverlay.session = arView.session
    coachingOverlay.goal = .horizontalPlane
    coachingOverlay.frame = arView.frame
    arView.session.run(config, options: [])
    arView.addSubview(coachingOverlay)

在ARSession的回调中增加背景墙和球体Entity,这儿一致运用ModelEntity,它默许挂载了ModleComponent、CollisionComponent、PhysicsBodyComponent和PhysicsMotionComponent组件,具有物理模拟和磕碰的能力,契合咱们的要求。

    public func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
    // 增加四周背景墙
    let shap : ShapeResource = .generateBox(width: 0.1, height: 1.2, depth: 3)
  let meshSphere: MeshResource = .generateBox(width: 0.1, height: 1.2, depth: 3)
    let material = SimpleMaterial(color:.red,isMetallic: **false**)
    let wallEntity = ModelEntity(mesh: meshSphere, materials: [material2], collisionShape: shap, mass: 0.04)
    wallEntity.physicsBody?.mode = .static
    wallEntity.physicsBody?.material = .generate(friction: 1, restitution: 0.01)
    wallEntity.transform.translation = [planeEntity.transform.translation.x-0.5,planeEntity.transform.translation.y+0.5, 0]
    wallEntity.name = "wall"
    planeAnchor.addChild(wallEntity)
    }
    // 增加待磕碰彩球
    ...

这儿需求解说一下刚体的物理特点,即physicsBodyMode:

  • .static 为永久不会移动,咱们AR场景中的底部,和四周的背景墙的便是这个特点。
  • .kinematic 用户操控身体运动,可以了解为人物类游戏中你操作的移动的人物。
  • .dynamic 力和磕碰操控物体进行运动,也便是AR场景下的发射的小球的特点。

磕碰检测

    // 订阅磕碰事情 Combine结构
    subscribes.append(scene.subscribe(to: CollisionEvents.Began.self, on: fireBallEntity) { event in
        if (A.name == "box" && B.name == "ball") || (B.name == "box" && A.name == "ball") {
        if A.name == "box" {
            A.name = "score"
            A.model?.materials = [SimpleMaterial(color: .black, isMetallic: **false**)]
            score = score + 1
            scoreLabel.text = "得分\(score)"
        }
        if B.name == "box" {
            B.name = "score"
            B.model?.materials = [SimpleMaterial(color: .black, isMetallic: false)]
            score = score + 1
            scoreLabel.text = "得分\(score)"
        }
        }
    })

手指(食指)识别

// 1. 创立检测器,设置回调
let request = VNDetectHumanHandPoseRequest(completionHandler:handDetectionCompletionHandler)
// 2. AR数据调用检测
public func session(_ session: ARSession, didUpdate frame: ARFrame) {
    if request == nil { return }
    let pixelBuffer = frame.capturedImage
    DispatchQueue.global().async {
      let handler = VNImageRequestHandler(cvPixelBuffer:pixelBuffer, orientation: .up, options: [:])
      do {
        try handler.perform([(request)!])
      } catch let error {
        print(error)
      }
    }
  }
// 3. 检测食指,与白球方位匹配,发射
func handDetectionCompletionHandler(request: VNRequest?, error: Error?) {
    guard** let observation = request?.results?.first as? VNHumanHandPoseObservation else { return }
    /* TIP:指尖。
    DIP:指间远端关节或指尖后的第一个关节。
    PIP:指间近关节或中心关节。
    MIP:掌指关节坐落手指底部,与手掌相连。
    .thumb 拇指
    .indexFinger 食指
    .middleFinger 中指
    .ringFinger 无名指
    .littleFinger 小指
    */
    guard let indexFingerTip = try? observation.recognizedPoints(.all)[.indexTip],
       indexFingerTip.confidence > 0.3 else {return}
    let normalizedIndexPoint = VNImagePointForNormalizedPoint(CGPoint(x: indexFingerTip.location.y, y: indexFingerTip.location.x), Int(UIScreen.main.bounds.width), Int(UIScreen.main.bounds.height))
    if let entity = arView.entity(at: normalizedIndexPoint) **as**? ModelEntity, entity == fireBallEntity {
      DispatchQueue.main.async {
            // 发射
        arView.fire()
      }
    }
    recentIndexFingerPoint = normalizedIndexPoint
  }

发射

发射只需求在小球实体增加一个力,设置方向和方向的参阅实体即可。

fireBallEntity.addForce([0,8,-40], relativeTo: planeEntity)

End

本文仅仅粗略介绍一部分结构的核心概念,之前的AR运用的生态相对来说还不老练,从市面上的AR落地场景来看,我们也仅仅做了简略的尝试(拍照器AR特效、AR拜年、AR地图找人/店/路、AR试鞋、AR小游戏等),随着visionOS 软件开发包,期待一些有意思有价值的运用出现。。。