ARKit

ARKit 教學:如何搭配SceneKit來建立一個簡單的ARKit Demo

ARKit 教學:如何搭配SceneKit來建立一個簡單的ARKit Demo
ARKit 教學:如何搭配SceneKit來建立一個簡單的ARKit Demo
In: ARKit, Swift 程式語言

擴增實境(Argument Reality)來囉!記得寶可夢(Pokemon Go)嗎?它當然也是擴增實境的代表之一!Apple終於在iOS11將擴增實境帶進來,也因為iOS11,未來將會有數不清的iPhones和iPads就會搭載AR功能,這將會讓 ARKit成為世界最大的平台,是的,如果你對建置擴增實境的Apps有興趣,讓你就來對地方了。

目標

本教學主要會開發一個ARkit Demo App,並應用SceneKit來協助你熟悉基礎的ARKit。

是時候讓你開始沉浸在本篇教學內,並讓你了解如何一步一步建構出ARKit App,且透過你手上的裝置與AR世界互動。

本篇教學的想法主要是學習AR與利用API來建置一個APP,藉由教學的步驟,你將會一步步了解ARKit在實體裝置上是如何與神奇的3D物件來互動的。

在開始前,請了解本篇教學僅是以基礎功能應用為主。

你需要準備的

進入本篇教學前,建議你已有對iOS的基礎開發的能力,這屬於中階程度的教學,並且,我們將需要Xcode9以上的版本。

為了測試你的ARKit App,你得需要一個可兼容Apple的ARkit的裝置,建議有Apple A9處理器以上等級的裝置。

現在請確認你已具備上述需求,並準備開始進行,以下是我將會帶你走過:

  • 建立一個新的ARKit apps專案
  • 設定ARKit SceneKit View
  • 將ARSCNView與View Controller結合
  • 連接IBOutlet
  • 設定ARSCNView Session
  • 允許相機使用權限
  • 將3D物件加到ARSCNView
  • 加入手勢判斷功能到ARSCNView
  • 從ARSCNView移除物件
  • 加入多樣物件到ARSCNView

建立一個新的ARKit apps專案

再來,打開Xcode,在Xcode的菜單中,選擇File > New > Project… ,然後選擇Single View App並按下next,其實Xcode也有內鍵ARKit的範例App,但你仍可以使用Single View App來開發AR app。

arkit-1

你可以自行命名你想要的專案名稱,我是命名為ARKitDemo,再按下next來完成新的專案。

設定ARKit SceneKit View

現在請打開Storyboard,請在右下角的Object Library找到ARKit SceneKit View,將它拖拉至你的View Controller。

然後將你的ARKit SceneKit View的尺寸拉滿整個View Controller,它應該會呈現如下方:

讚喔!這樣的話,ARKit SceneKit View就是我們要呈現擴增實境的SceneKit內容的位置。

連接IBOutlet

我們目前仍在Main.storyboard位置,請往介面右上方找到toolbar,並開啟Assistant Editor,現在將ARKit匯入到ViewController.swift檔位置:

import ARKit

接著請按住control並在ARKit ScenKit的View上拖到至ViewController.swift檔,當連接到時,請指定為IBOutlet,並命名為sceneView,對了,請放心地將didReceiveMemoryWarning()這個方法刪除,我們不會在本篇教學使用到它。

Creating an outlet variable for the scene view

設定ARKit SceneKit View

想看看,若想要我們的App在一開始執行時就能透過相機看到真實世界,並能偵測我們的週遭環境,這其實是相當驚人的科技!如今Apple已經幫開發者建立一套擴增實境的功能,我們並不需要再多花時間從無到有的重新設計,所以,謝謝Apple!讓我們能擁抱ARKit。

好的!現在是時候來設定ARkit SceneKit View了,請在ViewController的類別下插入下列程式碼:

override func viewWillAppear(_ animated: Bool) {

    super.viewWillAppear(animated)
    let configuration = ARWorldTrackingConfiguration()
    sceneView.session.run(configuration)

}

並在viewWillAppear(_:)方法內,我們將初始化AR configuration,它稱為ARWorldTrackingConfiguration,這是一個可以執行world tracking的功能設定,等等!你一定會問什麼是world tracking?來看一下Apple的官方文件說明:

“World tracking可提供裝置上六個自由度軌跡,來找到場景所需的特徵點,world tracking也會啟用performing hit-tests against the frame. 當這個單元暫停後,Tracking將不會再執行。”

“World tracking provides 6 degrees of freedom tracking of the device. By finding feature points in the scene, world tracking enables performing hit-tests against the frame.
Tracking can no longer be resumed once the session is paused.”

-Apple官方文件

所以簡單說明world tracking可以追踨裝置的方位與位置,它也可以經由裝置的相機來偵測真實世界的地平面。

最後一段程式碼,AR單元( Session)主要是管理動作追踨與相機影像處理內容。我們需要執行這個configuration

接下來,我們來加入另一個方法到ViewController內:

override func viewWillDisappear(_ animated: Bool) {

    super.viewWillDisappear(animated)
    sceneView.session.pause()

}

viewWillDisappear(_:)方法中,我們主要做的是當view在關閉時,設定AR單元就會同時停止追踨動作與處理圖像內容。

##允許相機使用權限

在我們要執行我們的App之前,我們需要告知我們使用者,我們得使用相機來進行擴增實境的應用,這是一個從iOS10就開始的必要詢問告知動作,也因此,請打開info.plist。然後在空白區域點選右鍵,並選擇Add row,在key下選用Privacy –  Camera Usage Description,然後在Value下寫下For Augmented Reality

過來,請確認此時你已經做好剛剛所教的一切。

請拿起你的裝置,並連線到你的Mac,來第一次建立與執行在Xcode的專案,此時這個App將會詢問你能否允許有打開相機的權限。請點按OK。若選擇Don’t allow,代表App不能使用相機來做想要執行的事情。

using camera

現在你應該能夠看到你相機畫面了。

我們也算是設定好我們的sceneView單元,並能執行world tracking,是時候進入令人興奮的階段了!擴增實境!

##將3D物件加到ARSCNView

話不多說,直接進入擴增實境,我們將要一個立方體(box),那我們先將下列程式碼加到你的ViewController類別。

func addBox() {

    let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)

    let boxNode = SCNNode()
    boxNode.geometry = box
    boxNode.position = SCNVector3(0, 0, -0.2)
    
    let scene = SCNScene()
    scene.rootNode.addChildNode(boxNode)
    sceneView.scene = scene

}

現在來解釋一下我們做了些什麼。

我們先要來建立一個立方體box的外型,1個Float = 1公尺。

接下來,我們建立一個點位boxNode物件,這個點位可代表位置與一個物件在3D空間的座標,但對它自己而言,他本身不會有可以看到的內容,需要協助它添加資訊。

所以我們需要在這個點位來建立一個形狀,並給予一些可視化的內容。先將立方體box的參數設為點位boxNode的幾何資訊,我們再給我們的點位一個位置,然而這個位置和相機有關係,以正x軸而言,是右邊;負x軸是左邊,正Y軸是上方,負Y軸是下方,而正Z軸是往後,負Z軸是往前。

接著,我們要來建立一個場景,這是一個應用SceneKit的場景功能來顯示在視圖上,過來加入我們的boxNode做為場景的初始根點位,然而初始根點位在一個場景中,是SceneKit用來定義與真實世界的座標系統的方式。

正常來說,我們的場景現在會有了一個立方體了,這個立方體會位在相機畫面的正中間,和相機的距離會有0.2公尺。

最後,讓我們的sceneView來顯示我們剛建立的場景。

現在請在viewDidLoad()加入addBox()的方法:

override func viewDidLoad() {

    super.viewDidLoad()
    addBox()

}

建立並執行這個App,你應該可以看見一個飄浮在空中的立方體囉!

你現在也可以重新簡化addBox()的方法:

func addBox() {

    let box = SCNBox(width: 0.05, height: 0.05, length: 0.05, chamferRadius: 0)

    let boxNode = SCNNode()
    boxNode.geometry = box
    boxNode.position = SCNVector3(0, 0, -0.2)
    
    sceneView.scene.rootNode.addChildNode(boxNode)

}

這會更容易了解在做些什麼事了吧。

好的!要繼續加入手勢了!

##加入手勢辨識方法到ARSCNView

addBox()的方法下,請加入下列程式碼:

func addTapGestureToSceneView() {

    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.didTap(withGestureRecognizer:)))
    sceneView.addGestureRecognizer(tapGestureRecognizer)

}

在這裡,我們先初始化點擊手勢辨識方法物件,並將target設為ViewController,也就是self,在action內設為didTap(withGestureRecognizer:),然後我們再將點擊手勢辨識這個物件也加入在scenView內。

是時候來做些點擊手勢辨識方法物件內的呼叫方法

#從ARSCNView移除物件

ViewController.swift加入下列程式碼:

@objc func didTap(withGestureRecognizer recognizer: UIGestureRecognizer) {

    let tapLocation = recognizer.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(tapLocation)
    guard let node = hitTestResults.first?.node else { return }
    node.removeFromParentNode()
  
}

在這裡,我們建立了didTap(withGestureRecognizer:)的方法,目的是我們要獲得使用者在sceneView的點擊位置,並可看得到我們觸擊的node。

然後,我們將從hitTestResults中移除掉第一個點位,如果hitTestResults內沒得到任何一個點位,我們將會當初第一個點擊的點位,也是做為parent node的,就移除。

在我們測試物件移除時,請更新viewDidLoad()的方法,並加入一個呼叫addTapGestureToSceneView()的方法:

override func viewDidLoad() {

    super.viewDidLoad()

    addBox()
    addTapGestureToSceneView()

}

現在如果你若能建置與執行你的專案,你應該可以點擊box node並能從scene view移除它。

不過我們感覺回到了起點。

沒關係!那我們來加多點物件。

加入多樣物件到ARSCNView

現在我們的立方體感覺有點孤獨,我們也來多做一點立方體吧,我們將在一些特徵點上加入物件。

所以什麼是特徵點呢?

根據Apple官方說明,對特徵點的定義:

此點由ARKit自動從一個連續的表面中自動辨識,但不會有另一相對的依靠點。

它其實是依真實世界的實物表面上偵測特徵點,所以,我們回到如何實現增加立方體呢,在我們開始前,在ViewController類別的程式碼最下方建立一個extension。

extension float4x4 {

    var translation: float3 {
        let translation = self.columns.3
        return float3(translation.x, translation.y, translation.z)
    }

}

這個exetension建立了一個float3的矩陣,它可同時加入x, y和z三個參數。

因此,我們下一步要修改addBox()

func addBox(x: Float = 0, y: Float = 0, z: Float = -0.2) {

    let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)

    let boxNode = SCNNode()
    boxNode.geometry = box
    boxNode.position = SCNVector3(x, y, z)
    
    sceneView.scene.rootNode.addChildNode(boxNode)

}

基本上,我們加入了參數來初始化addBox()的方法,同時也給它一個初始值,這代表我們可以不用在viewDidLoad()呼叫addBox()的方法時,就得寫入特定x, y和z座標值。

好的!

現在我們需要修改didTap(withGestureRecognizer:)的方法,我們想要當真實世界的某一點被偵測到時,我們就能加入一個物件。

所以回到我們的guard let的程式碼描述,在else之後,並在return之前,請加入下列程式碼:

{
let hitTestResultsWithFeaturePoints = sceneView.hitTest(tapLocation, types:         .featurePoint)

if let hitTestResultWithFeaturePoints = hitTestResultsWithFeaturePoints.first {

    let translation = hitTestResultWithFeaturePoints.worldTransform.translation
    addBox(x: translation.x, y: translation.y, z: translation.z)
    }
}  

來解釋我們想要達成的方式。

首先,我們先要有一個hit test,很像是我們第一次測試,除了這個,我們清楚定義.featurePoint屬於types

參數。types參數要求hit test經由AR單元的相機圖像來搜尋真實世界的實體物或是表面。它內含許多類型,但本教學目前只針對特徵點。

經由特徵點的hit test後,我們可以安全地移除第一次hit test的結果,這觀念很重要,因為不會一直都有特徵點,ARKit並不會一會偵測真實世界的實體物與表面。

如果第一次hit test能成功移除,然後我們就將轉換矩陣類型matrix_float4x4float3,因為我們之前已增加了一個extension來完成此功能,有興趣的話,我們也可以自行修正x, y和z實際世界座標。

然後,我們在一特徵點上輸入x, y和z來加入一個立方體。

你的didTap(withGestureRecognizer:)方法應如下所示:

@objc func didTap(withGestureRecognizer recognizer: UIGestureRecognizer) {

    let tapLocation = recognizer.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(tapLocation)
    guard let node = hitTestResults.first?.node else {
        let hitTestResultsWithFeaturePoints = sceneView.hitTest(tapLocation, types: .featurePoint)
        if let hitTestResultWithFeaturePoints = hitTestResultsWithFeaturePoints.first {
            let translation = hitTestResultWithFeaturePoints.worldTransform.translation
            addBox(x: translation.x, y: translation.y, z: translation.z)
        }
        return
    }
    node.removeFromParentNode()

}

測試最終版App

現在是時候按 Run 鍵執行項目以測試最終版的 App!

總結

恭喜你,和我們走了這麼久 完成本篇教學,ARKit還有還有許多功能需要我們繼續挑戰,我們只是學了一些表面功夫。

我希望你享受本篇ARKit的介紹,我也期待你會建構出屬於你的ARKit App。

關於完整的範例專案,你可以在GitHub找到。

如果你還想學習更多有關ARKit的開發,請分享此教學給你朋友並讓我知道。

譯者簡介:Oliver Chen-工程師,喜歡美麗的事物,所以也愛上Apple,目前在iOS程式設計上仍是新手,正研讀Swift與Sketch中。生活另一個身份是兩個孩子的爸,喜歡和孩子一起玩樂高,幻想著某天自己開發的App,可以讓孩子覺得老爸好棒!。聯絡方式:電郵[email protected]

原文Building a Simple ARKit Demo with SceneKit in Swift 4 and Xcode 9

作者
Jayven N
年輕、富創意的Jayven熱愛手機程式設計,善於發掘和凸顯不平凡處,透過文章抒發其獨特性。閒時喜歡做健身、看UFC。希望了解Jayven更多,可以到訪他的Medium平台或在LinkedIn跟他聯繫。
評論
更多來自 AppCoda 中文版
透過 Reality Composer 和 RealityKit 輕鬆地創建 3D AR Apps
ARKit

透過 Reality Composer 和 RealityKit 輕鬆地創建 3D AR Apps

RealityKit 是 2019 年推出的新框架,用於實作高性能 3D 模擬和渲染功能,而 Reality Composer 就讓初學者無需編寫任何程式碼,都可以輕鬆地創建互動的 AR 體驗。在這篇文章中,你將學會使用這兩個框架,構建互動的 3D AR App。
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。