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能探測到合適的平面,你就能看到一個黃色的水平面了.
移動一下設備,你會注意到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.