ARKit+iOS 11原生開發入門(上)

特別說明:

本教程基於ARKit和Swift的Native開發,並非基於Unity或Unreal。內容翻譯改編自iOS 11 By Tutorials的beta試讀章節,版權歸原作者所有。此外,由於作者在寫作該教程時基於iOS 11 beta5之前的版本,部分API已被棄用或更改(這就是當小白鼠的代價~),本人基於iOS 11 beta8做了部分細節上的調整(2017年8月31日),具體以蘋果最新的官方文檔為主。

原文作者似乎是在iPad上做的測試,本人的開發和測試環境參考如下:

測試設備:iPhone 6s Plus

測試系統:iOS 11 beta 8

開發環境-工具:Xcode 9 beta6

開發環境-硬體:MacBook Pro(Retina,15-inch,Mid 2014), macOS Sierra(10.12.6)

前言:

在不久之前,主流的增強現實技術看起來還是一個很遙遠的夢想。因此當蘋果在WWDC2017開發者大會上推出全新的iOS框架ARKit時,整個業界再次為之振奮。我們只需擁有一部iOS設備就可以運行AR應用,完全不需要任何的第三方配件。

ARKits的展示效果令人震驚。在本教程結束的時候,我們將創建一個名為HomeHero的應用,它可以幫我們來測量、規劃和可視化的呈現室內的裝修設計。

開始前的準備

打開本章的Starter項目文件夾(鏈接: pan.baidu.com/s/1jI3Plq 密碼: w3kc),找到並打開HomeHero.xcodeproj文件。在選擇了HomeHero項目文件和TARGETS後,在General 選項卡中選擇Team信息。

注意:和其它類型的應用開發不同,使用ARKit開發應用時Simulator是沒什麼作用的,我們需要在一台具有A9處理器或更好配置的iOS設備上進行測試。

目前搭載了Apple A9和A10處理器的只有iPhone 6s, iPhone 6s Plus, iPhone SE,iPhone7,iPhone 7 Plus,兩代iPad Pro和最新的iPad。

當然,即便發布的iPhone7s ,iPhone7s Plus,iPhone Pro肯定在此系列。

點擊編譯並在設備上運行,可以看到類似下面的畫面。

目前這個畫面平淡無奇,它只是在界面上放置了幾個普通的UIButton而已。接下來我們需要配置ARKit並進行渲染。不過在此之前,我們首先需要了解ARKit的各種組成元素。

ARKit會使用設備上的攝像頭和運動傳奇器件來檢測設備位置隨時間的變化。攝像頭所捕捉的圖像還會被用來識別景深信息、平面,以及光照條件。因此,在展示使用ARKit開發的應用時,需要確保背景具備良好的光照條件,同時有大量的物體和豐富的紋理。ARKit的演算法非常精確,因此我們可以在它的幫助下來測量真實世界中的物體。

ARKit session

讓我們假定iOS設備是個機器人:它有自己的眼睛,也可以感受到自己的運動。那麼ARSession就是機器人的大腦,它可以通過API來交流自己所看到和所感受到的。

ARSession中包含了兩個組成部分:

1.ARAnchor:

這個類用來表達某個物體在真實世界中的位置和朝向。我們可以手動給session添加anchor,以跟蹤物體。當ARSession在檢測特定的物體時,也會自動添加ARAnchor。舉例而言,當我們打開自動平面檢測時,就會自動添加ARAnchor的子類ARPlaneAnchor物體。

2.ARFrame:

ARKit需要捕捉視頻幀,並使用ARFrame來分析每個視頻幀的運動數據,並返回信息摘要。ARFrame中包含了所捕捉到的視頻信息,光線評估數據,和所有跟蹤到的ARAnchor對象。ARFrame中包含了ARCamera,它代表了一個物理的攝像頭及其位置。ARFrame中還包含了所檢測到的feature points(功能點),其中包含了真實世界3D坐標的有意思的特性。視頻幀上的信息越多,那麼特徵點也越多。

ARSession包含了一個ARSessionDelegate代理對象,可以讓程序隨時了解正在發生的事情。

Session configuration

我們需要ARConfiguration來運行ARSession。我們不推薦直接使用ARConfiguration,因為它只會檢測設備的旋轉,而非位置信息。但是有一點需要說明的是,它支持沒有配置A9處理器的設備,因此僅當我們需要支持之前更老的設備時,才需要用到它。正常情況下我們應使用ARWorldTrackingConfiguration(ARConfiguration的子類),它可以跟蹤所有角度的運動,並給出最佳的結果。

ARWorldTrackingConfiguration讓我們可以選擇:

1.Light Estimation(光線評估)

通過額外的計算,可以評估光照條件,並以ARFrame對象的形式返回。它可以幫助虛擬物體更好的契合真實世界的光照。

2.Plane Detection(平面檢測)

可以檢測真實世界中的平面,並通過添加ARPlaneAnchor對象的方式來自動追蹤。當然到目前為止,ARKit僅支持對水平面的支持。ARKit目前支持對地板、桌子、沙發等平面的支持。當我們移動攝像頭的時候,ARKit可以持續追蹤數據的變化,包括平面的位置和延伸等,或是合併為一個平面。

Rendering(渲染)

之前做過類比,ARKit好比機器人的大腦,其中包含了它所觀察到的原始數據,但是它並不會渲染出任何東西。我們需要使用其它的渲染器來渲染出增強現實對象,如SceneKit,Metal或是SpriteKit。蘋果為此提供了ARSCNView,可以讓我們使用SceneKit來渲染ARKit所提供的數據。ARSCNView會創建一個ARSession實例對象,並將ARCamera映射到一個SCNCamera上,這樣當我們移動設備的時候,所渲染出的SceneKit對象也會隨之移動。通過這種方式,可以大大減輕我們的開發工作量,你很快就會意識到這一點。

開始設置ARKit

為了放置和渲染虛擬物體,我們首先需要設置ARSession和ARSCNView。在Xcode中打開HomeHeroViewController.swift。這個starter項目中已經包含了一個ARSCNView,它和HomeHeroViewController的sceneView屬性關聯在一起。

讓我們創建一個遵從ARSCNViewDelegate的HomeHeroViewController擴展如下:

extension HomeHeroViewController:ARSCNViewDelegate{n}n

在該方法中,ARSCNView將更新ARSession和renderer的狀態。我們將在後面具體實現這個方法。

在HomeHeroViewController類中添加以下方法:

func runSession(){n//1nsceneView.delegate = self;n//2nlet configuration = ARWorldTrackingConfiguration()n//3nconfiguration.planeDetection = .horizontaln//4nconfiguration.isLightEstimationEnabled = truen//5nsceneView.session.run(configuration)n//6n#if DEBUGnsceneView.debugOptions = ARSCNDebugOptions.showFeaturePointsn#endifn}n

在以上方法中,我們設置並運行了ARSession,下面依次解釋下各行代碼的作用:

1.將HomeHeroViewController註冊為ARSCNView的代理,後續我們將用它來渲染物體。

2.使用ARWorldTrackingSessionConfiguration來充分利用所有的運動信息,並給出最佳的結果。需要注意的是,它只支持蘋果A9處理器,或者更高。

3.打開自動水平面檢測。我們將用詞來渲染平面,從而進行測試,以及將物體放置在真實世界之中。

4.打開光線評估運算。ARSCNView將自動使用該功能,並基於所測算的真實世界光照條件來給物體打光。

5.run(_:options)開啟ARKit進程,並開始捕捉視頻畫面。該方法將會讓設備請求使用相機,如果用戶拒絕該請求,那麼ARKit將無法工作。

6.ASRCNView還有一個額外的功能,也即渲染出特徵點,使用該行代碼可以在調試的過程中查看特徵點。

為了在HomeHeroViewController的viewDidLoad()方法中調用runSession()方法,我們還需要更改當前的viewDidLoad()方法如下:

override func viewDidLoad() {nsuper.viewDidLoad()nrunSession()ntrackingInfo.text = ""nmessageLabel.text = ""ndistanceLabel.isHidden = truenselectVase()n}n

此時,當視圖載入的時候,將開啟ARKit的進程。

在??上編譯運行該應用,首先我們需要允許使用相機。ARSCNView將自動為你完成這些事情:顯示背景中所捕捉到的視頻圖像,並渲染特徵點。如果沒有ARSCNView,很難想像你自己該如何完成以上的工作。

繪製平面

在放置物體對象之前,我們需要了解ARKit是如何認識這個世界的。可能大家對特徵點已經有所了解了,但是特徵點並非真實世界的精確模型與寫照。幸運的是,ARKit提供了另一種更為精確的世界描述方式:plane(平面)。

ARPlaneAnchors將會被自動添加到ARSession之中,而ARSCNView則會自動將ARPlaneAnchor對象添加到SCNNode的節點中。這樣就給開發者帶來了極大的便利,因為我們只需實現ARSCNViewDelegate方法,然後渲染新的平面。

接下來讓我們實現HomeHeroViewController的ARSCNViewDelegate擴展的renderer(_:didAdd:for:)方法如下:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {n//1nDispatchQueue.main.async {n//2nif let planeAnchor = anchor as? ARPlaneAnchor{n//3n#if DEBUGn//4nlet planeNode = createPlaneNode(center: planeAnchor.center, extent: planeAnchor.extent)n//5nnode.addChildNode(planeNode)nn#endifn}n}n}n

當ARSession識別到一個新的平面時,ARSCNView就自動為該平面添加一個ARAnchor,並調用renderer(_:didAdd:for:)代理方法。這裡詳細解釋下以上代碼的作用:

1.在一個單獨的queue中調用了Renderer代理方法。為了避免多線程的問題,最簡單的方法就是將其分發到主線程隊列中。

2.這行代碼用來檢測新添加的ARAnchor是否是ARPlaneAnchor的子類。

3.表示此處的內容僅用於調試輸出。

4.createPlaneNode(center:extent:)是項目中所提供的一個輔助方法。它是一段和SceneKit相關的代碼,用於使用從ARPlaneAnchor中獲取的指定中心的和延伸範圍來創建一個藍色的平面SCNNode。

5.這裡的node參數是一個空的SCNNode,將會被ARSCNView自動添加到場景之中,其坐標跟anchor參數相關。我們只需要使用addChildNode(_:)方法將一個子對象關聯到該空白節點即可,子對象(比如這裡的平面)就會自動顯示在正確的位置。

除此之外,我們還需要考慮當平面的大小或者位置發生變化時,或者一起被刪除時的情況。

因此,我們需要實現renderer(_:didUpdate:for:)和renderer(_:didRemove:for:)兩個代理方法。

其代碼如下:

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {nDispatchQueue.main.sync {nif let planeAnchor = anchor as? ARPlaneAnchor{n//1nupdatePlaneNode(node.childNodes[0], center: planeAnchor.center, extent: planeAnchor.extent)n}n}n}nfunc renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor) {nguard anchor is ARPlaneAnchor else{nreturnn}n//2nremoveChildren(inNode: node)n}n

以上代碼的主要作用就是更新平面的狀態。其中renderer(_:didUpdate:for:)方法將在相關的ARAnchor更新時被調用,而renderer(_didRemove:for:)方法則將在相關的ARAnchor被刪除時被調用。這裡是對加了數字的代碼行的解釋:

1.更新子節點,也就是此前在renderer(_:didAdd:for:)方法中所添加的平面節點。updatePlaneNode(_:ceenter:extent:)是起始項目中所提供的一個輔助方法,用來根據ARPlaneAnchor中的更新信息來更新平面的坐標和大小。

2.當對應的ARAnchorPlane唄刪除時從節點中刪除平面。removeChildren(inNode:)方法也是起始項目中所提供的輔助方法,具體細節大家可以自己研究。

此時編譯並運行應用,就可以看到在真實的世界中出現可視化的平面了。

需要注意的是,在測試時如果沒有立即檢測出平面,需要耐心等待一點時間。

創建AR對象

好了!現在我們可以進入虛擬世界,然後按照自己的想法來改造世界了。首先我們將要學習如何在所檢測的平面上放置物體。ARSCNView提供了非常有用的hit檢測方法,因此我們將使用該方法來檢測手指在虛擬世界中觸碰所檢測的平面上的點。

在HomeHeroViewController.swift中添加新的方法如下:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {n//1nif let hit = sceneView.hitTest(viewCenter, types: [.existingPlaneUsingExtent]).first{n//2nsceneView.session.add(anchor: ARAnchor(transform:hit.worldTransform))nreturnn}n}n

以上方法將在hit test的結果點中添加一個新的anchor。這裡對數字編號的代碼注釋如下:

1.hitTest(_:types:)方法將返回指定平面坐標和類型的所有hit test結果。這裡我們傳遞了一個viewCenter參數作為屏幕的中心坐標點,也就是灰色小點的位置。viewCenter是起始項目中所提供的輔助屬性。接下來使用existingPlaneUsingExtent這個hit test選,從而說明我們需要獲取已存在平面的hit test結果信息,並保留平面的有限大小(範圍)。

2.如果我們獲取到結果,則使用ARSession中的add(anchor:)方法來創建一個anchor錨點,以表示物體在真實世界中所放置的點。

以上代碼雖然添加了anchor,但是並不會渲染出任何東西。ARSCNView將在添加了新的ARAnchor時調用renderer(_:didAdd:for:)代理方法。我們將在該方法中處理對新物體的渲染。

更改之前的renderer(:_:didAdd:for:)方法的代碼如下:

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {n//nDispatchQueue.main.async {nif let planeAnchor = anchor as? ARPlaneAnchor{n#if DEBUGnlet planeNode = createPlaneNode(center: planeAnchor.center, extent: planeAnchor.extent)nnode.addChildNode(planeNode)n#endifn//1n}else{n//2nswitch self.currentMode{ncase .none:nbreakn//3ncase .placeObject(let name):n//4nlet modelClone = nodeWithModelName(name)n//5nself.objects.append(modelClone)n//6nnode.addChildNode(modelClone)n//7ncase .measure:nbreakn}n}n}n}n

以上代碼將在真實世界中放置所選擇的模型,具體的數字代碼行注釋如下:

1.else 意味著該ARAnchor並非ARPlaneAnchor的子類,而是一個普通的ARAnchor實例,通過touchesBegan(_:with:)方法添加。

2.currentMode是已經添加到起始項目中的HomeHeroController屬性,它代表當前的UI狀態:其中placeObject代表選擇的是物體按鈕,而measure則代表所選擇的是測量按鈕。switch代碼將根據UI狀態的不同執行不同的代碼。

3.placeObject包含了一個關聯的string值,其代表到3D模型.scn文件的路徑。我們可以在Models.scnassets中查看所有的3D模型。

4.nodeWithModelName(_:)方法使用指定的路徑名稱來創建一個新的3D模型SCNNode,該方法是起始項目中所提供的輔助方法。

5.將節點擴展到起始項目中所提供的objects數組中。

6.最後,我們將新的物體節點添加到代理方法中的SCNNode。

7.關於測量工具,我們將在後續具體來實現。

編譯並在??上運行項目,將灰色的點指向所檢測出的平面上,並觸碰屏幕,即可將物體添加到場景之中。

未完待續。


推薦閱讀:

小記一次用 ARkit 設計和開發 Mobile AR 籃球小項目
如何評價蘋果在 WWDC 2017上發布的 ARKit?

TAG:ARKit | iOS开发 | 苹果公司AppleInc |