26-[译]用 ARKit 2 创建一个博物馆 App

530 阅读20分钟

说明

ARKit系列文章目录

资料下载

你是否曾经站在博物馆展台前面,不满足于艺术品或手工艺品前面的小卡片所展示的内容,想要知道更多?要是有一个这样的 app 该有多好。现在,你可以用 ARKit 2 来自己制作一个带有图片/物体检测与追踪的 app。

为了让体验更加有趣,ARKit 允许 app 添加动态的虚拟内容到真实世界物体上。它允许我们为真实世界的地方和物品创建一个交互式的引导 app。是博物馆,画廊,游乐场和学校等地的完美配套 app。它可以让任何地方“活起来”,提供一个动态的或定制的体验。

在本教程中,你将构建一个TriassicLoupe(三叠纪放大镜),一个用在自然历史博物馆里恐龙展览的配套 app。这个概念来自珠宝商的放大镜;这个 app 会在用户指向展品的时候显示出隐藏的细节。如果你周围没有任何恐龙,请不要担心 - 你可以使用普通的家用物品作为占位。

最终应用程序在信息图像上显示一段简短的动画。它还会在复制品旁边显示有关恐龙的信息。该 app 还将为该物体添加一些可怕的声音效果。

该应用程序将 ARKit 与 SceneKit(iOS的3D图形框架)结合使用。你会看到 ARKit 使用 SceneKit 完成所有繁重的工作。对于图片追踪和物体追踪,您将只使用非常基本的 SceneKit 功能,本教程中您从中学到的所有内容都是通用的。了解有关 SceneKit 的更多信息将使您能够构建更丰富的应用程序,但这超出了本教程的范围。

开始

ARKit 依赖于A9或更高版本处理器的内置功能。它使用了机器学习,后置摄像头,以及视频和图像处理。这意味着 ARKit 应用程序需要在iPhone 6s或更新版本上运行,并且它们无法在模拟器中运行。

使用超长的Lightning线缆或者设置设备以通过 Wi-Fi 连接到Xcode方便调试。 ARKit 需要移动一点才能获得一张好的世界地图。世界地图是 ARKit 对物理空间的意识。它是一系列特征点,尺寸,方向和锚点。

稍后你会需要扫描一些图像。显示器上显示的图像应该也适用,但如果您在扫描图像时遇到问题,则可能需要打印图像以获得更好的效果。

要开始使用,请使用本教程顶部或底部的“下载材料”按钮下载入门项目。 .zip文件包含一个帮助项目,包含将在教程中使用的素材,以及初始项目和最终项目。

打开入门项目。应用程序本身非常简单,只需一个ViewController即可添加所有逻辑。有一个辅助结构体,DinosaurFacts,其中包含一些关于一些恐龙的基本信息。

如果你构建并运行,你会看到一个黑屏,因为你尚未连接ARKit会话。

创建图片检测

首先要做的是创建图片检测。图片检测可能是ARKit最简单的功能。要创建图片检测器,您所要做的就是提供带有图像副本的图像跟踪会话。这些提供的图像称为参考图片

添加参考图片

TriassicLoupe 使用含有信息标志的艺术作品作为参考图像。当用户将应用程序指向其中一个标志时,该应用程序将添加恐龙图像叠加层。

与其他应用程序图像一样,增强现实(AR)参考图像存在于素材目录asset catalog中。参考图像有点特殊,因为它们需要专门为ARKit分组。

打开Assets.xcassets 点击下面的 + 按钮。

从弹出菜单中,选择New AR Resource Group以创建新组。将其重命名为AR Images,因为该组将保留参考图像。

在Finder中,从下载的材料中打开Dinosaur Images文件夹。将每个图像文件逐个拖动到Xcode中的AR Images中。完成后,您应该有三个带黄色警告三角形的图像。

如果参考图像作为参考质量不好,Xcode会发出警告。这可能是因为它们太小或者没有足够的特征或对比度。具有大量空白空间,颜色太少或缺乏独特形状的图像是难以检测的。

在这种情况下,警告是“不支持的配置”警告。这是因为参考图像必须具有非零的正宽度。 AR参考图像需要您指定其实际尺寸!

在资产库中选择stegosaurus(剑龙)图像。然后选择Attributes inspector

将单位更改为英寸。接下来,输入宽度4.15。当你这样做时,根据纵横比,高度将自动变为2.5711!这些数字就是您桥接虚拟世界和真实世界的地方。

这两张图片,配置值如下:

  • trex:Inches, width: 6.3333, height: 4.75
  • triceratops: Inches, width: 5, height: 2.8125

当你输入尺寸后,警告就消失了。

这些图像对应于所包含的Dinosaurs.key Keynote文件的幻灯片。每张幻灯片代表一个信息标语牌,旁边是博物馆展示。在美国信纸大小的纸张上打印时,这个尺寸就是物理图像尺寸。

注意:某些图像实际上是在Keynote幻灯片中裁剪出来的。只要保持大小和宽高比相同,ARKit 完全能够识别并匹配。

这些图像都是不同的风格,以展示ARKit系列的一小部分。这里有两件事情:1。图像中有足够的形状和对比度。 2.真实世界中的图片平坦,光线充足,不反光。

书页上,壁纸上或在镜相打印出的图片是难以识别的。照片,绘画或插图可以很好地识别出来。如果参考图片不够友好,Xcode会发出警告。这样就无需在运行时猜测!

现在,是时候继续编写代码来寻找这些图像了。

添加图片追踪

ViewController.swift中,在注释下添加一个新变量:// Add configuration variables here:

private var imageConfiguration: ARImageTrackingConfiguration?

这将设置一个变量,以便在创建后保留图像跟踪配置。 现在,查找setupImageDetection()并添加以下代码:

imageConfiguration = ARImageTrackingConfiguration()

这会将该实例变量设置为新的ARImageTrackingConfiguration。顾名思义,此类是一个ARKit配置,用于检测和跟踪图像。 在该行下面,添加以下内容:

guard let referenceImages = ARReferenceImage.referenceImages(
  inGroupNamed: "AR Images", bundle: nil) else {
      fatalError("Missing expected asset catalog resources.")
  }
imageConfiguration?.trackingImages = referenceImages

这将使用您刚刚在素材目录中创建的AR Images组中的图像,创建一个ARReferenceImage集。然后,将它们作为要跟踪的图像列表添加到配置中。

注意:图像检测最适用于资源组中不超过25个图片。如果您的博物馆有超过25个展品,您可以创建多个资源组,并在用户在建筑物周围移动时切换它们。

要使用配置,请将以下内容添加到viewWillAppear(_ :)

if let configuration = imageConfiguration {
  sceneView.session.run(configuration)
}

这将使用imageConfiguration启动ARKit会话。一旦运行,ARKit将处理相机数据以检测参考图片。

要确保全部启动,请将以下内容添加到viewDidLoad()的底部:

setupImageDetection()

最后,为了平衡会话运行,在viewWillDisappear(_ :)添加:

sceneView.session.pause()

当视图消失时,这会暂停会话。ARKit会话会因为相机调用,视频处理和渲染,而把电池耗尽。在我们的单视图应用程序中,这不是什么大问题,但是尊重用户的设备并在没有显示时暂停会话总是一个好主意。

处理检测到的图片

一旦检测到图像,AR会话就会将ARImageAnchor添加到其世界地图中。当发生这种情况时,您将在渲染器处获得回调renderer(_:didAdd:for:)

ViewController.swift的底部找到此函数并添加以下代码:

DispatchQueue.main.async { self.instructionLabel.isHidden = true }
if let imageAnchor = anchor as? ARImageAnchor {
  handleFoundImage(imageAnchor, node)
}

此代码检查是否为图像锚点添加了新添加的节点。这意味着在现实世界中检测到图像。 将handleFoundImage(_:_:)函数替换为:

let name = imageAnchor.referenceImage.name!
print("you found a \(name) image")

let size = imageAnchor.referenceImage.physicalSize
if let videoNode = makeDinosaurVideo(size: size) {
  node.addChildNode(videoNode)
  node.opacity = 1
}

这将从锚点的参考图片中获取图像的名称和大小。您在素材目录中指定了这些值。使用该尺寸,调用辅助函数以在检测到的图像之上创建视频播放器。

要创建视频节点,请将makeDinosaurVideo(size :)的内容替换为:

// 1
guard let videoURL = Bundle.main.url(forResource: "dinosaur",
                                     withExtension: "mp4") else {
  return nil
}

// 2
let avPlayerItem = AVPlayerItem(url: videoURL)
let avPlayer = AVPlayer(playerItem: avPlayerItem)
avPlayer.play()

// 3
NotificationCenter.default.addObserver(
  forName: .AVPlayerItemDidPlayToEndTime,
  object: nil,
  queue: nil) { notification in
    avPlayer.seek(to: .zero)
    avPlayer.play()
}

// 4
let avMaterial = SCNMaterial()
avMaterial.diffuse.contents = avPlayer

// 5
let videoPlane = SCNPlane(width: size.width, height: size.height)
videoPlane.materials = [avMaterial]

// 6
let videoNode = SCNNode(geometry: videoPlane)
videoNode.eulerAngles.x = -.pi / 2
return videoNode

此功能创建一个视频播放器并将其放入一个大小适合图像的SceneKit节点。它通过以下方式实现:

  1. 从资源包中抓取视频。这有一个适用于所有恐龙的简单动画。但您可以根据图像锚点的名称为每种恐龙类型提供不同的视频。
  2. 为该视频创建和启动AVPlayer
  3. AVPlayer 实例并不会自动循环播放。此通知块通过侦听播放器完成来循环视频。然后, 它会返回开头并重新开始播放。
  4. SceneKit 不使用UIViews, 而是使用节点来渲染场景。不能直接添加AVPlayer。相反, 视频播放器可以用作节点的纹理或 "材质"。这将把视频帧映射到关联的节点。
  5. 检测到的图像是平坦正方形 (即平面), 因此覆盖的节点是与检测到的图像大小相同的SCNPlane。这架飞机被装饰成视频作为其纹理。
  6. 创建出的实际节点将成为场景的一部分。这就需要在 x 轴上进行翻转, 以便它正确显示给用户。

尝试一下

最后的最后, 是时候构建并运行了!但是, 首先, 打印幻灯片Dinosaurs.key中至少一张恐龙。将其放置在光线充足的区域中 (垂直或水平).

构建并运行该应用程序。接受相机权限并显示视频后, 请将其指向打印页面。它可能需要两只稳定的手来检测图像。完成后, 您将在控制台中看到一个注释, 并在屏幕上显示一个动画叠加。

不幸的是, 如果会话启动, 但它没有检测到图像, 那也没有错误消息。大多数情况下, ARKit 不保证能找到该图像, 因此它不被视为错误。只要素材目录中没有关于图像的警告, 就应该最终检测到该图像。

添加物体检测和追踪

现在, 您已经看到了图像检测如何工作。接下来, 您将向应用添加物体检测。从开发人员的角度来看, 物体检测的工作原理几乎相同。主要区别在于它将寻找三维物体而不是平面图像。设置物体检测稍微复杂一些。参考物体的创建也比较复杂。

回顾一下, 以下是使用图像和物体检测的步骤:

  1. 创建参考对象。
  2. 将参考对象放入素材集的AR Resources组中。
  3. 创建一个 ARKit 会话。
  4. 加载参考图片/物体,并设置会话来检测他们。
  5. 启动 session.
  6. 等待锚点被添加时的回调。
  7. 添加交互节点到场景中,或采取其他措施。

物体检测

另一个有用的 ARKit 函数是物体检测和跟踪。TriassicLoupe 检测已知物体并标记它们。在现实中, 这些将是恐龙在一个透视或恐龙骨架。对于本教程, 您将使用手头上的任何东西。

选择参考物体

检测物体所需的第一件事是参考物体。通过图像检测, 您可以创建、扫描或拍摄图像。但是, 对于3D 对象, 参考物体很难构造.

ARKit 提供了自己的 API, 用于使用 iPhone 扫描它们来创建参考物体。TriassicLoupe 不直接使用此项, 因为它只能检测到一组已知的对象。您可以使用 Apple 提供的实用程序提前扫描它们。

如果可以, 您应该下载 Apple 的对象扫描程序项目Object Scanner project。它也包括在项目下载ScanningAndDetecting3DObjects文件夹中, 为您的方便。请注意, 在您阅读此内容时, 包含的项目可能已过期。

此应用程序扫描对象, 并允许您导出. arobject文件。然后, 您可以将此文件作为素材导入到 Xcode 项目中。成功的扫描需要有合适的对象和良好的照明。适当的对象是:

  • 实体不可变形。
  • 拥有很多细节纹理(比如形状和颜色)。
  • 无反射或透明效果。
  • 尺寸应在是垒球和椅子中间。

您可能没有在您的家中的3D 恐龙显示, 因此您可以使用任何家庭物体来完成教程。比较合适的物体是饼干罐、动作图或植物。将物体放在平坦且光照良好的表面上 。

创建参考物体

构建并运行此应用程序到真机设备上。为获得最佳效果, 请使用 iPhone 8、8 + 或 X, 它具有足够的处理能力, 在执行扫描时保持良好的帧速率。

  1. 将手机摄像头对准物体。对象周围应出现一个黄色框。一旦对象位于框的中间, 请点按Next
  2. 通过移动边界框并长按拖动边线来调整边框大小。让该框应仅包含被扫描物体。与新的测量应用一样, 此扫描仪使用 ARKit 来测量对象的真实世界大小。一旦对象处于中间, 点击Scan按钮。
  3. 将手机对准物体,绕着物体扫描。一定要有多个角度, 以及上面和两侧。黄色填充框, 意味着已经扫描获取足够的信息。应尽量得到完整的覆盖。
  4. 下一步将设置锚点。这个点用来控制模型的节点几何体是如何与真实世界互动的。对于本教程, 确切的位置并不重要。试着把它放在物体的中央底部平面上。准备好后按 Finish
  5. 点击Export按钮,用 AirDrop,文件共享或电子邮件将.arobject文件发送到你的设备上。

重复上述步骤,得到另外两个物体的模型。

导入参考物体

回到Assets.xcassets,创建一个新的AR Resource Group。命名为AR Objects.

将每个. arobject文件拖到此组中。您将在扫描时看到物体的小照片预览。重命名对象以匹配这些恐龙名称: brachiosaurus腕龙、iguanodon禽龙和velociraptor迅猛龙。

与图像检测不同, 您不必指定大小, 因为它已由物体扫描过程测量。

寻找物体

下一步是设置配置项以查找这些对象。在ViewController.swift的顶部, 在imageConfiguration定义下, 添加:

private var worldConfiguration: ARWorldTrackingConfiguration?

这将创建一个变量来存储世界追踪配置。这个配置是物体检测必需的。与图像检测不同的是, 没有针对物体的配置。

下一步,将setupObjectDetection()方法替换为:

worldConfiguration = ARWorldTrackingConfiguration()

guard let referenceObjects = ARReferenceObject.referenceObjects(
  inGroupNamed: "AR Objects", bundle: nil) else {
  fatalError("Missing expected asset catalog resources.")
}

worldConfiguration?.detectionObjects = referenceObjects

这将创建ARWorldTrackingConfiguration的实例。此配置是功能最齐全的 ARKit 配置。它可以检测水平和垂直平面以及 3D 物体。它使用后置摄像头以及所有运动传感器来计算真实世界的虚拟呈现.

创建配置后, 从素材目录加载参考物体, 并将参考物体设置为detectionObjects。一旦检测到, 当 ARKit 将其锚点添加到场景中时, 您将获得相应的回调。

viewDidLoad中,将最后一行改为:

setupObjectDetection()

这样,就会将图片检测改为了物体检测。

用下面代码替换viewWillAppear(_:)中的内容,以使用新配置项启动会话:

super.viewWillAppear(animated)
if let configuration = worldConfiguration {
  sceneView.debugOptions = .showFeaturePoints
  sceneView.session.run(configuration)
}

这同时还激活了ARSCNDebugOptions.showFeaturePoints调试选项。这会在屏幕上为 ARKit 检测到的特征点处放置黄色点。这有助于在您再次运行应用时进行调试。在物体上显示的点越多, 检测就越容易。

注意: 场景视图一次只能运行一个配置, 但您可以随时替换该配置。如果要更新选项或切换配置类型, 只需运行新配置即可。除非明确清除, 否则会话将保留与检测到的要素和锚点相同的世界地图。如果要切换一组检测物体, 请执行此操作。例如, 如果用户从恐龙展览移动到天文学展览, 他们可以看到一组不同的物体。

找到物体

与图片检测中类似,当 ARKit 检测到一个 3D 物体时,会向世界地图中添加一个锚点,并向场景树中添加一个节点。

修改renderer(_:didAdd:for:):

DispatchQueue.main.async { self.instructionLabel.isHidden = true }
if let imageAnchor = anchor as? ARImageAnchor {
  handleFoundImage(imageAnchor, node)
} else if let objectAnchor = anchor as? ARObjectAnchor {
  handleFoundObject(objectAnchor, node)
}

这将保留以前对图像锚点的处理, 只添加检查以查看新锚点是否为物体锚点。如果它是一个物体锚点, 这意味着检测到一个物体!然后, 将节点和锚点交给帮助方法。

说到这里,还要将handleFoundObject(_:_:)中的内容替换为:

// 1
let name = objectAnchor.referenceObject.name!
print("You found a \(name) object")

// 2
if let facts = DinosaurFact.facts(for: name) {
  // 3
  let titleNode = createTitleNode(info: facts)
  node.addChildNode(titleNode)

  // 4
  let bullets = facts.facts.map { "• " + $0 }.joined(separator: "\n")

  // 5
  let factsNode = createInfoNode(facts: bullets)
  node.addChildNode(factsNode)
}

这段代码收集有关找到的物体的信息。用户就可以读取恐龙节点旁边,添加的文本节点上的额外信息。查看代码:

  1. referenceObject的名称在素材目录中,并匹配恐龙的名称。
  2. DinosaurFact是个帮助类,描述每个已知的恐龙。它有一个facts的列表。
  3. 此帮助器函数创建具有恐龙名称的文本节点, 并将其添加到场景中。此文本将看起来像浮动在在物体上方。
  4. 这个小圆点符号,拼接在每个 fact 前面, 将它们组合成一个单独的、行分隔的字符串。SCNText 节点可以有多行, 但只接受一个字符串。
  5. 这个帮助类创建文本节点, 它将显示在对象的旁边,并被添加到场景中。

展示文本节点

现在该深入了解 SceneKit 的文本节点了。

handleFoundObject(_:_:)下面,添加帮助函数来创建文本节点:

private func createTitleNode(info: DinosaurFact) -> SCNNode {
  let title = SCNText(string: info.name, extrusionDepth: 0.6)
  let titleNode = SCNNode(geometry: title)
  titleNode.scale = SCNVector3(0.005, 0.005, 0.01)
  titleNode.position = SCNVector3(info.titlePosition.x, info.titlePosition.y, 0)
  return titleNode
}

这将创建具有恐龙名称的文本节点。SCNText是描述字符串形状的几何图形。它允许你创建可放置在场景中的形状。与物体相比, 默认文本尺寸很大, 因此缩放titleNode会将其缩小到合理大小.

这个position用来将文本与物体的中心对齐。由于物体的大小可能不同, 因此需要为每个单独的恐龙指定尺寸。您可以为自己的物体调整DinosaurFacts.swift中的值。默认情况下, SceneKit 的尺寸以米为单位。

接下来, 添加其他帮助器函数:

private func createInfoNode(facts: String) -> SCNNode {
  // 1
  let textGeometry = SCNText(string: facts, extrusionDepth: 0.7)
  let textNode = SCNNode(geometry: textGeometry)
  textNode.scale = SCNVector3(0.003, 0.003, 0.01)
  textNode.position = SCNVector3(0.02, 0.01, 0)

  // 2
  let material = SCNMaterial()
  material.diffuse.contents = UIColor.blue
  textGeometry.materials = [material]

  // 3
  let billboardConstraints = SCNBillboardConstraint()
  textNode.constraints = [billboardConstraints]
  return textNode
}

和先前的帮助类相似,但有几点不同:

  1. 首先, 与上一个帮助函数一样, 这将创建一个带有节点的文本几何图形, 并将其缩放到合理的大小。
  2. 使文本显示蓝色。蓝色是通过创建一个新的材料, 其diffuse漫反射内容设置为蓝色设置的。节点的材质是用来让场景渲染器找出物体对光照的反应。漫反射属性类似于基本外观。在这里, 它被设置为蓝色, 但也可以改为图像或视频, 正如你以前看到的。
  3. SCNBillboardConstraint是一个有用的约束, 它一直面向节点, 以便文本始终面向用户。这提高了可读性;在移动时, 您不必移动到一个尴尬的角度来查看文本。

构建并运行应用, 然后指向其中一个扫描物体。

物体检测可能需要多次尝试。在物体周围移动: 前后, 两侧等, 给 ARKit 了解形状的最佳机会有助于识别物体。

ARKit 检测到物体后, 应用程序将在物体旁边显示信息。请注意, 标题浮动在物体上方。如果您的物体高一些,高了几英寸, 则必须在DinosaurFacts.swift中调整titlePosition。文本朝向将沿扫描期间设置的原点方向.

与标题不同, 信息项目符号会与相机一起移动, 以便它们始终面向您。

同时进行图片追踪和物体追踪

此时, 您已使用物体追踪替换了图像追踪。ARWorldTrackingConfiguration是一种超集配置;它支持与ARImageTrackingConfiguration相同的图像跟踪.

要回到图像追踪, 将以下行添加到setupObjectDetection的底部。

guard let referenceImages = ARReferenceImage.referenceImages(
  inGroupNamed: "AR Images", bundle: nil) else {
    fatalError("Missing expected asset catalog resources.")
}
worldConfiguration?.detectionImages = referenceImages

这将加载与以前相同的一组参考图片, 但将它们设置为世界跟踪配置的detectionImages. 现在再构建运行一次, 您将看到恐龙海报上的动画和物体上的信息文本。

添加声音

让应用程序变得很酷的一种方法,就是给他一些带位置的音频。是时候添加一些可怕的恐龙的声音了!

在文件顶部, 在配置下面添加一个新变量:

lazy var audioSource: SCNAudioSource = {
  let source = SCNAudioSource(fileNamed: "dinosaur.wav")!
  source.loops = true
  source.load()
  return source
}()

这里创建了一个SCNAudioSource,它会持有 SceneKit 中用到的音频数据。这些音频数据是从对应的音频文件夹中加载的。

下一步,在handleFoundObject(_:_:)的最末尾,添加下面一行代码:

node.addAudioPlayer(SCNAudioPlayer(source: audioSource))

Voilà! 就是这样。默认情况下,一个SCNAudioPlayer处理 3D 音效的所有细节。

再次构建并运行。将相机对准物体。当物体被识别后,就开始播放惊悚的音效。要获得最佳的效果,建议戴上耳机。这样,当你绕着物体走动的时候,就能听到声音位置变化带来的声音变化。

总结

是时候对博物馆说晚安了。但这并不意味着 ARKit 的乐趣必须停止。

这个应用程序本身是很难完全补充完成的。您可以在场景中显示更多信息和更多媒体类型。而且, 你可以添加更多的展品, 不仅仅是恐龙。你在前面看到过一些提示: 在区域之间移动时切换配置, 并与 Core Location 结合, 以自动确定用户位于何处。

ARKit 还允许共享世界地图。这意味着博物馆观众可以分享个人物品的注释和评论。

这里有很多免费和高级 ARKit 内容, 参考一下了解你还能做些什么。

如果您想了解有关 SceneKit 的更多信息, 请查阅Swift SceneKit 教程或查看我们的书籍

资料下载

译者注:《3d apple games by tutorials》的读书笔记免费参考这里