跨越屏幕的界限,蘋果 ARKit 初探">跨越屏幕的界限,蘋果 ARKit 初探

分類:IT技術 時間:2017-09-30

0. 前言

作為一名剛入門的 iOS 開發者,前陣子稍稍研究了一下最新發布的 ARKit,然後結合幾個其他開源項目做成了一個 ARGitHubCommits 。前天在上海第 8 次 T 沙龍上分享了一個《ARKit 初探》的 topic,現在將它寫成文章,以便瀏覽。

1. ARKit 簡介

下面是 蘋果開發者官網 ARKit 頁面 的一段介紹:

  • iOS 11 引入了新的 ARKit 框架,讓您輕松創建無可比擬的 iPhone 和 iPad 增強現實體驗。 通過將數字對象和信息與您周圍的環境相融合,ARKit 為 App 解開了屏幕之縛,帶領著它們跨越屏幕的界限,讓它們以全新的方式與現實世界交流互動。

可見,蘋果在 AR 的市場上應該是做了很多準備。不僅有本文要介紹的 ARKit,在最新發布的 iPhone X 中也對攝像頭做了優化,配備了前置景深攝像頭,將 AR 和面部識別結合起來。所以,AR 可能將會是未來幾年內的一個重要發展方向。

2. 設備要求

蘋果在硬件上也做了一些努力。我們可以從官網的介紹中得出以下幾個信息:

  • 建立在優秀的硬件設施和算法上,ARKit 采集的現實世界數據相對來說比較精準(比 Google 的 ARCore 要好些)。

  • 可以利用 ARKit 來探測水平面,然後可以在水平面上放置小的物體。

  • ARKit 會根據周圍光線的亮度自動調節虛擬物體的亮度和陰影、紋理等信息。

當然,要運行 ARKit,在硬件上也有一些要求。一定是要具備 A9 及以上的處理器(iPhone 6s 為 A9 處理器)的設備才可以運行 AR。軟件上,如果要開發 ARKit App,那麽要有 Xcode 9 和 iOS 11 SDK。

當你做好了一切準備,那就讓我們進入 ARKit 的世界!

3. AR 工作流程

AR 工作流程

上圖解釋的是 ARKit 的工作流程。其中藍色表示 ARKit 負責的部分,綠色表示 SceneKit 負責的部分。當然,建立虛擬世界也可以使用其他的框架,比如 SpriteKit、Metal,本文將以 SceneKit 為例子進行講解。

  1. 首先,ARKit 利用攝像頭拍攝現實場景的畫面,然後 SceneKit 用來建立虛擬世界。

  2. 建立好了以後,ARKit 負責將現實世界和虛擬世界的信息融合,並渲染出一個 AR 世界。

  3. 在渲染的同時,ARKit 要負責以下三件事:

  • 維持世界追蹤

    指的是當你移動攝像頭,要去獲取新的現實世界的信息。

  • 進行場景解析

    指的是解析現實世界中有無特征點、平面等關鍵信息。

  • 處理與虛擬世界的互動

    指的是當用戶點擊或拖動屏幕時,處理有沒有點擊到虛擬物體或者要不要進行添加/刪除物體的操作。

由此可見,ARKit 主要做的事是: 捕捉現實世界信息、將現實和虛擬世界混合渲染、並且時刻處理新的信息或者進行互動

理解了 AR 的工作流程後,讓我們來看看 ARKit 中一些重要的類的職責。

4. ARKit 和 SceneKit 關系圖

ARKit 和 SceneKit 關系圖

上面是 ARKit 和 SceneKit 的關鍵的類的關系圖。其中 ARSCNView 是繼承自 SCNView 的,所以其中關於 3D 物體的屬性、方法都是 SCNView 的(如 SCNScene、SCNNode 等)。

下面簡單介紹一下 ARKit 中各個類是如何協作的。

ARSCNView

最頂層的 ARSCNView 主要負責綜合虛擬世界(SceneKit)的信息和現實世界的信息(由ARSession 類負責采集),然後將它們綜合渲染呈現出一個 AR 世界。

ARSession

ARSession 類負責采集現實世界的信息。這一行為也被稱作世界追蹤。它主要的職責是:

  • 追蹤設備的位置以及旋轉,這裏的兩個信息均是相對於設備起始時的信息。

  • 追蹤物理距離(以“米”為單位),例如 ARKit 檢測到一個平面,我們希望知道這個平面有多大。

  • 追蹤我們手動添加的希望追蹤的點,例如我們手動添加的一個虛擬物體。

它采集到的現實世界信息以 ARFrame 的形式返回。

當然,為了有一個比較好的追蹤效果,要滿足以下要求:

  • 運動傳感器 不能停止工 作。如果運動傳感器停止了工作,那麽就無法拿到設備的運動信息。根據我們之前提到的世界追蹤的工作原理,毫無疑問,追蹤質量會下降甚至無法工作。

  • 真實世界的場景需要 有一定特征點可追蹤 。世界追蹤需要不斷分析和追蹤捕捉到的圖像序列中特征點,如果圖像是一面白墻,那麽特征點非常少,那麽追蹤質量就會下降。

  • 設備移動速度 不能過快 。如果設備移動太快,那麽 ARKit 無法分析出不同圖像幀之中的特征點的對應關系,也會導致追蹤質量下降。

總的說來,就是要 提示用戶移動手機,且速度不能太快,要在略微復雜的場景中探測

ARFrame

ARFrame 包含了兩部分信息:ARAnchor 和 ARCamera。其中,

  • ARCamera 指的是當前攝像機的位置和旋轉信息。這一部分 ARKit 已經為我們配置好,不用特別配置。

  • ARAnchor 指的是現實世界中的錨點,具體解釋如下:

ARAnchor

  • 可以把 ARAnchor(錨點)理解為真實世界中的某個點或平面,anchor 中包含位置信息和旋轉信息。拿到 anchor 後,可以在該 anchor 處放置一些虛擬物體。

  • 與 SCNNode 可以綁定

  • 它有一個子類:ARPlaneAnchor,專門指的是一個代表水平面的錨點。

ARConfiguration

指的是 ARSession 將如何追蹤世界,有以下幾種子類:

  • ARWorldTrackingConfiguration(6 DOF)

    是 ARSession 的默認配置,以6個自由度(x y z軸上的位移及繞著三個軸的旋轉)追蹤現實錨點和虛擬物體。

  • AROrientationTrackingConfiguration(3 DOF)

    以 3 個自由度(沒有位移,只有旋轉)追蹤,但是被蘋果文檔聲明不推薦使用。這樣的追蹤質量將比較差。

  • ARFaceTrackingConfiguration(iPhone X Only)

    以面部識別來追蹤,只有裝備了 True Depth 前置景深攝像頭的 iPhone X 才能使用。

而且,如果要開啟平面檢測,需要加入以下語句:

let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal

總結

用一段話總結的話,就是 ARSCNView 結合 SCNScene 中的虛擬世界信息和 ARsession 捕捉到的現實世界信息,渲染出 AR 世界。ARConfiguration 指導 ARSession 如何追蹤世界,追蹤的結果以 ARFrame 返回。ARFrame 中的 ANAnchor 信息為 SceneKit 中的 SCNNode 提供了一些放置的點,以便將虛擬節點和現實錨點綁定。

5. ARKit 中的 Delegate

ARSCNViewDelegate

先介紹 ARSCNView 的代理:ARSCNViewDelegate,他有以下幾個回調方法。

func renderer(SCNSceneRenderer, nodeFor: ARAnchor)

當 ARSession 檢測到一個錨點時,可以在這個回調方法中決定是否給它返回一個 SCNNode。默認是返回一個空的 SCNNode(),我們可以根據自己的需要將它改成只在檢測到平面錨點(ARPlaneAnchor)時返回一個錨點,諸如此類。

func renderer(SCNSceneRenderer, didAdd: SCNNode, for: ARAnchor)
func renderer(SCNSceneRenderer, willupdate: SCNNode, for: ARAnchor)
func renderer(SCNSceneRenderer, didUpdate: SCNNode, for: ARAnchor)
func renderer(SCNSceneRenderer, didRemove: SCNNode, for: ARAnchor)

以上方法會在為一個錨點已經添加、將要更新、已經更新、已經移除一個虛擬錨點時進行回調。

ARSessionDelegate

ARSession 類也有自己的代理:ARSessionDelegate

func session(ARSession, didUpdate: ARFrame)

在 ARKit 中,當用戶移動手機時,會實時更新很多 ARFrame。這個方法會在更新了 ARFrame 時,進行回調。它可以用於類似於始終想維持一個虛擬物體在屏幕中間的場景,只需要在這個方法中將該節點的位置更新為最新的 ARFrame 的中心點即可。

func session(ARSession, didAdd: [ARAnchor])
func session(ARSession, didUpdate: [ARAnchor])
func session(ARSession, didRemove: [ARAnchor])

如果使用了 ARSCNViewDelegate 或 ARSKViewDelegate,那上面三個方法不必實現。因為另外的 Delegate 的方法中除了錨點以外,還包含節點信息,這可以讓我們有更多的信息進行處理。

6. 一些實踐

下面就幾種常用場景給出一些示例代碼。

添加物體

添加物體可以有以下兩種方式:自動檢測並添加或者手動點擊添加。

自動檢測並添加

主要利用了 ARSCNViewDelegate 中的回調方法:

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
    if let planeAnchor = anchor as? ARPlaneAnchor {
        let node = SCNNode()
        node.geometry = SCNBox(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.y), length: CGFloat(planeAnchor.extent.z), chamferRadius: 0)
        return node
    }
    return nil
}

這段代碼的含義是:如果找到了一個平面錨點,那就返回一個和該平面錨點的長寬高分別相同的白色長方體節點。當你移動手機尋找平面時,一旦找到,便會有一個白色平面出現在屏幕上。

手動點擊添加

ARKit 允許用戶在畫面中點擊,來和虛擬世界互動。

比如我們之前添加了一個 UITapGestureRecognizer,selector 是如下方法:

@objc func didTap(_ sender: UITapGestureRecognizer) {
    let location = sender.location(in: sceneView)
    let hitResults = sceneView.hitTest(location, types: .featurePoint)
    if let result = hitResults.first {
        let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
        let boxNode = SCNNode(geometry: box)
        boxNode.position = SCNVector3(x: result.worldTransform.columns.3.x,
                                  y: result.worldTransform.columns.3.y,
                                  z: result.worldTransform.columns.3.z)
        sceneView.scene.rootNode.addChildNode(boxNode)
    }
}

這其中用到了一個 ARHitTestResult 類,它可以檢測用戶手指點擊的地方有沒有經過一些符合要求的點/面,有如下幾種選項:

  • featurePoint

    返回當前圖像中 Hit-testing 射線經過的 3D 特征點。

  • estimatedHorizontalPlane

    返回當前圖像中 Hit-testing 射線經過的預估平面。

  • existingPlaneUsingExtent

    返回當前圖像中 Hit-testing 射線經過的有大小範圍的平面。

  • existingPlane

    返回當前圖像中 Hit-testing 射線經過的無限大小的平面。

上面一段代碼的含義是:首先記錄用戶點擊的位置,然後判斷有沒有點擊到特征點,並將結果按從近到遠的順序返回。如果有最近的一個結果,就生成一個長寬高都為0.1米的立方體,並把它放在那個特征點上。

其中將 ARHitTestResult 信息轉換成三維坐標,用到了 result.worldTransform.columns.3.x(y,z)的信息。我們不深究其中原理,只需知道它的轉換方法就可以了。

更新物體位置

這時可以使用 ARSessionDelegate 的代理方法:

func session(_ session: ARSession, didUpdate frame: ARFrame) {
    if boxNode != nil {
        let mat = frame.camera.transform.columns.3
        boxNode?.position = SCNVector3Make((mat.x) * 3, (mat.y) * 3, (mat.z) * 3 - 0.5)
    }
}

也就是當更新了一個 ARFrame,就把一個之前建立好的 SCNNode 的位置更新為 frame 的中心點。這裏 * 3 是為了放大移動的效果。註意,這裏也用到了上面所說的 worldTransform 和 SCNVector3 的轉換方法。

7. ARGitHubCommits 思路

有了以上的知識基礎,我們可以用以下思路來構建這個項目:

獲取 GitHub 的 Commits 數據。

建立 ARSCNView,開始 ARSession。

提示用戶移動手機,探測水平面。

探測成功後,在 ARSCNView 中的 SCNScene 中加入各個 SCNNode。

具體的代碼,歡迎參考 GitHub 。

8. 參考

下面是一些可以參考的文章/GitHub鏈接,僅供參考:

  • https://developer.Apple.com/cn/arkit/

  • https://developer.apple.com/documentation/arkit

  • http://blog.csdn.net/u013263917/article/details/72903174

  • https://mp.weixin.qq.com/s/DxPHo6j6pJQuhdXM_5K4qw

  • https://github.com/olucurious/Awesome-ARKit

  • https://github.com/songkuixi/ARGitHubCommits

  • 微博:@滑滑雞

  • GitHub: songkuixi


Tags: ARKit 蘋果 初探 屏幕 可以 界限

文章來源:


ads
ads

相關文章
ads

相關文章

ad