ARKit系列文章目录node
译者注:本文是Raywenderlich上《ARKit by Tutorials》免费章节的翻译,是原书第7章.原书7~9章完成了一个时空门app.
官网原文地址www.raywenderlich.com/195361/buil…ios
本文是咱们书籍ARKit by Tutorials中的第7章,“建立你的时空门”.这本书向你展现了如何用苹果的加强现实框架ARKit,来构建五个沉浸式的,好看的AR应用.开始吧swift
经过这一系列教程,你将用ARKit和SceneKit实现一个时空门应用.时空门类的app能够用于教育目的,好比一个太阳系虚拟浏览应用,或者一些休闲活动,好比享受一场虚拟的沙滩假期.数组
在这个应用中,你将在现实世界中的某个水平面上,放置一个通往充满将来感的房间的虚拟门.你能够走进走出这个房间,探索里面有什么.xcode
在该教程中,你将创建时空门应用的基础.在本教程中,你将学会如何:session
你准备好创建通往另外一个世界的通道了么?app
在Xcode中,打开starter工程, Portal.xcodeproj.建立并运行工程,你会看到一个空白屏幕. 框架
啊,是的,一个空白充满机遇的画布!
打开Main.storyboard再展开Portal View Controller Scene async
PortalViewController是应用启动后呈现给用户的界面.它包含一个ARSCNView来显示相机预览画面.还包含了两个UILabels来提供说明和反馈给用户.ide
如今,打开PortalViewController.swift.在这个文件中,你将看到下面的变量,它们表明了storyboard中的元素:
// 1
@IBOutlet var sceneView: ARSCNView?
// 2
@IBOutlet weak var messageLabel: UILabel?
// 3
@IBOutlet weak var sessionStateLabel: UILabel?
复制代码
让咱们看看其中的内容:
注意:ARKit会处理全部的传感器和相机数据,但它不会实际去渲染任何虚拟内容.要在你的场景中渲染内容,可使用与ARKit协同的各类渲染器,好比SceneKit or SpriteKit.
ARSCNView是苹果提供的一个框架,你能够轻易将ARKit中数据与SceneKit融合在一块儿.使用ARSCNView会有不少好处,这就是为何你要在本教程的项目中用它.
在starter工程中,你将会在Helpers分组下看到不少工具类.你会在app后面的开发中用到它们.
第一步是用相机来捕捉视频流.为此,你须要使用ARSCNView对象.
打开PortalViewController.swift并添加下面的方法:
func runSession() {
// 1
let configuration = ARWorldTrackingConfiguration.init()
// 2
configuration.planeDetection = .horizontal
// 3
configuration.isLightEstimationEnabled = true
// 4
sceneView?.session.run(configuration)
// 5
#if DEBUG
sceneView?.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
#endif
}
复制代码
代码说明:
如今,是时候创建labels的默认设置了.用下面的代码替换resetLabels():
func resetLabels() {
messageLabel?.alpha = 1.0
messageLabel?.text =
"Move the phone around and allow the app to find a plane." +
"You will see a yellow horizontal plane."
sessionStateLabel?.alpha = 0.0
sessionStateLabel?.text = ""
}
复制代码
这将messageLabel和sessionStateLabel设置好透明度和文本.记住,messageLabel是用于展现给用户说明,而sessionStateLabel是用于展现错误信息的,以防出错.
如今,添加runSession()到PortalViewController中的viewDidLoad() 里面:
override func viewDidLoad() {
super.viewDidLoad()
resetLabels()
runSession()
}
复制代码
这将会在app启动并加载视图时运行ARKit session.
接着,构建并运行app.不要忘记--你须要给app授于相机访问权限.
ARSCNView完成了繁重的相机视频捕捉和显示任务.由于是调试模式,你能够看到渲染出的特征点,它们造成了点云,显示出场景分析的中间结果.
先前,在runSession()中, 你设置了planeDetection为 .horizontal,这意味着你的app能探测水平面.你可以在ARSCNViewDelegate协议的代理回调方法中得到捕捉到的平面信息.
在PortalViewController中添加类扩展,实现ARSCNViewDelegate协议:
extension PortalViewController: ARSCNViewDelegate {
}
复制代码
在runSession() 末尾添加下面代码:
sceneView?.delegate = self
复制代码
这行代码将PortalViewController设置为sceneView对象的ARSCNViewDelegate代理.
ARPlaneAnchors会被自动添加到ARSession锚点数组中,而且ARSCNView自动将ARPlaneAnchor对象转换为SCNNode节点.
如今,要渲染这些平面,你须要作的是实现ARSCNViewDelegate代理方法:
// 1
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// 2
DispatchQueue.main.async {
// 3
if let planeAnchor = anchor as? ARPlaneAnchor {
// 4
#if DEBUG
// 5
let debugPlaneNode = createPlaneNode(
center: planeAnchor.center,
extent: planeAnchor.extent)
// 6
node.addChildNode(debugPlaneNode)
#endif
// 7
self.messageLabel?.text =
"Tap on the detected horizontal plane to place the portal"
}
}
}
复制代码
代码含义:
如今是时候创建帮助类的方法了.
建立一个新的Swift文件,命名为SCNNodeHelpers.swift.用来盛放渲染SCNNode对象相关的全部工具方法.
导入SceneKit到文件中:
import SceneKit
复制代码
如今,添加下面的帮助方法:
// 1
func createPlaneNode(center: vector_float3, extent: vector_float3) -> SCNNode {
// 2
let plane = SCNPlane(width: CGFloat(extent.x),
height: CGFloat(extent.z))
// 3
let planeMaterial = SCNMaterial()
planeMaterial.diffuse.contents = UIColor.yellow.withAlphaComponent(0.4)
// 4
plane.materials = [planeMaterial]
// 5
let planeNode = SCNNode(geometry: plane)
// 6
planeNode.position = SCNVector3Make(center.x, 0, center.z)
// 7
planeNode.transform = SCNMatrix4MakeRotation(-Float.pi / 2, 1, 0, 0)
// 8
return planeNode
}
复制代码
代码解读:
运行一下app,若是ARKit能探测到合适的平面,你就能看到一个黄色的水平面了.
ARKit会根据新发现的特征点来持续更新平面的位置和尺寸.要想接收这些更新,在PortalViewController.swift中添加下列的renderer(_:didUpdate:for:) 代理方法:
// 1
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
// 2
DispatchQueue.main.async {
// 3
if let planeAnchor = anchor as? ARPlaneAnchor,
node.childNodes.count > 0 {
// 4
updatePlaneNode(node.childNodes[0],
center: planeAnchor.center,
extent: planeAnchor.extent)
}
}
}
复制代码
解释:
打开SCNNodeHelpers.swift文件,添加下列代码:
func updatePlaneNode(_ node: SCNNode, center: vector_float3, extent: vector_float3) {
// 1
let geometry = node.geometry as? SCNPlane
// 2
geometry?.width = CGFloat(extent.x)
geometry?.height = CGFloat(extent.z)
// 3
node.position = SCNVector3Make(center.x, 0, center.z)
}
复制代码
代码解释:
如今你能够成功地更新平面的位置了,运行一下app.你会看到平面的尺寸和位置会随着探测到新的特征点而调整.
还有一个问题须要解决.一旦app检测到平面,若是你退出app再从新回来,你会看到前一个探测到的平面还在相机视图上,显示在其余物体前面;它已经再也不匹配先前的平面了.
要修复这个问题,你须要在ARSession被打断时移除平面节点.咱们会在下一章处理这个问题.
你可能还没意识到,但你已经踏上了建立一个时空门app的漫漫长路!是的,还有不少要作的事,可是你已经在进入虚拟空间的路上了.
本章节简单总结:
在下一章教程中,你将会学习如何处理session的打断,及在视图中使用SceneKit来渲染3D物体.点击这里来继续本系列教程的第2部分!
若是你喜欢本教程,能够来查看咱们的完整版书籍ARKit by Tutorials.