ARKit入门-建立第一个AR体验

翻译自 Building Your First AR Experience

创建一个运行AR session并利用平面检测使用SceneKit来放置3D内容的app。

概述

这个示例app运行一个 ARKit 现实追踪 session,并且在 SceneKit 视图中显示内容。为了演示平面检测,这个app仅放置一个 SCNPlane 对象来可视化每个检测到的 ARPlaneAnchor 对象。

配置并运行 AR Session

ARSCNView 类是一个 SceneKit 视图,包括一个 ARSession 对象,该对象用来管理创建AR体验所需的运动跟踪和图像处理。但是,要运行 AR Session,必须要提供一个 Session 配置。

ARWorldTrackingConfiguration 类提供高精度的运动跟踪,并且可以帮助你将虚拟内容放置在真实物体表面。要启动 AR Session,请使用你想要的选项(例如平面检测)来创建一个Session配置对象,然后在ARSCNView实例的session对象上调用run方法:

1
2
3
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
sceneView.session.run(configuration)

只有在屏幕上显示View的时候才运行session。

重要: 如果你的app的核心功能需要使用ARKit,请使用你app的Info.plist文件 UIRequiredDeviceCapabilities 中的 arkit 键,以确保你的app仅在支持ARKit的设备上可用。
如果AR只是你app的辅助功能,请使用 isSupported 属性来确定是否提供基于AR的功能。

在检测到的平面上放置3D内容

在设置好AR session后,可以使用 SceneKit 来放置虚拟内容在视图里。

当启用平面检测时,ARKit 为每个检测到的平面 添加和更新 anchors。默认情况下,ARSCNView 类为每个anchor 添加一个 ACNNode 对象到 SceneKit 场景。你的View的代理可以实现 renderer(_:didAdd:for:) 方法来向场景中添加内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Place content only for anchors found by plane detection.
// 将内容仅用于通过平面检测发现的anchor
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }

// Create a SceneKit plane to visualize the plane anchor using its position and extent.
// 创建一个SceneKit平面,以使用其位置和范围来可视化平面anchor
let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
let planeNode = SCNNode(geometry: plane)
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)

/*
`SCNPlane` is vertically oriented in its local coordinate space, so
rotate the plane to match the horizontal orientation of `ARPlaneAnchor`.
*/

/*
`SCNPlane` 在本地坐标空间中是垂直的,所以 旋转这个平面来匹配 ‘ARPlaneAnchor’ 的水平方向。
*/
planeNode.eulerAngles.x = -.pi / 2

// Make the plane visualization semitransparent to clearly show real-world placement.
// 使平面的可视化半透明,以清楚的显示真实事件的位置。
planeNode.opacity = 0.25

/*
Add the plane visualization to the ARKit-managed node so that it tracks
changes in the plane anchor as plane estimation continues.
*/
/*
将平面可视化添加到ARKit管理的节点中,以便于跟踪平面anchor的改变,随着平面估计的继续。

*/
node.addChildNode(planeNode)
}

如果将内容添加为与anchor相对应的节点的子节点,ARSCNView类会自动的移动改内容,因为ARKit会优化它对平面位置和范围的估计。为了显示估计平面的全部范围,该示例app还实现了renderer(_:didUpdate:for:) 方法,更新SCNPlane对象的大小来反映ARKit提供的估计值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
// Update content only for plane anchors and nodes matching the setup created in `renderer(_:didAdd:for:)`.
// 仅更新 平面的anchor ,和在`renderer(_:didAdd:for:)`中创建设置的nodes 匹配的nodes
guard let planeAnchor = anchor as? ARPlaneAnchor,
let planeNode = node.childNodes.first,
let plane = planeNode.geometry as? SCNPlane
else { return }

// Plane estimation may shift the center of a plane relative to its anchor's transform.
// 平面估计 可能会移动平面的中心,由于其相关联的anchor的中心发生变换。
planeNode.simdPosition = float3(planeAnchor.center.x, 0, planeAnchor.center.z)

/*
Plane estimation may extend the size of the plane, or combine previously detected
planes into a larger one. In the latter case, `ARSCNView` automatically deletes the
corresponding node for one plane, then calls this method to update the size of
the remaining plane.
*/
/*
平面估计可能会增大平面的尺寸,或者与之前检测到的平面结合成一个更大的平面。
在后一种情况下,`ARSCNView` 会自动的删除对应平面的节点,然后调用这个方法来更新剩下平面的大小。
*/
plane.width = CGFloat(planeAnchor.extent.x)
plane.height = CGFloat(planeAnchor.extent.z)
}