做爲一名剛入門的 iOS 開發者,前陣子稍稍研究了一下最新發布的 ARKit,而後結合幾個其餘開源項目作成了一個 ARGitHubCommits。前天在上海第 8 次 T 沙龍上分享了一個《ARKit 初探》的 topic,如今將它寫成文章,以便瀏覽。node
下面是蘋果開發者官網 ARKit 頁面的一段介紹:git
iOS 11 引入了新的 ARKit 框架,讓您輕鬆建立無可比擬的 iPhone 和 iPad 加強現實體驗。 經過將數字對象和信息與您周圍的環境相融合,ARKit 爲 App 解開了屏幕之縛,帶領着它們跨越屏幕的界限,讓它們以全新的方式與現實世界交流互動。github
可見,蘋果在 AR 的市場上應該是作了不少準備。不只有本文要介紹的 ARKit,在最新發布的 iPhone X 中也對攝像頭作了優化,配備了前置景深攝像頭,將 AR 和麪部識別結合起來。因此,AR 可能將會是將來幾年內的一個重要發展方向。算法
蘋果在硬件上也作了一些努力。咱們能夠從官網的介紹中得出如下幾個信息:bash
固然,要運行 ARKit,在硬件上也有一些要求。必定是要具有 A9 及以上的處理器(iPhone 6s 爲 A9 處理器)的設備才能夠運行 AR。軟件上,若是要開發 ARKit App,那麼要有 Xcode 9 和 iOS 11 SDK。session
當你作好了一切準備,那就讓咱們進入 ARKit 的世界!app
上圖解釋的是 ARKit 的工做流程。其中藍色表示 ARKit 負責的部分,綠色表示 SceneKit 負責的部分。固然,創建虛擬世界也可使用其餘的框架,好比 SpriteKit、Metal,本文將以 SceneKit 爲例子進行講解。框架
因而可知,ARKit 主要作的事是:捕捉現實世界信息、將現實和虛擬世界混合渲染、而且時刻處理新的信息或者進行互動。優化
理解了 AR 的工做流程後,讓咱們來看看 ARKit 中一些重要的類的職責。ui
上面是 ARKit 和 SceneKit 的關鍵的類的關係圖。其中 ARSCNView 是繼承自 SCNView 的,因此其中關於 3D 物體的屬性、方法都是 SCNView 的(如 SCNScene、SCNNode 等)。
下面簡單介紹一下 ARKit 中各個類是如何協做的。
最頂層的 ARSCNView 主要負責綜合虛擬世界(SceneKit)的信息和現實世界的信息(由ARSession 類負責採集),而後將它們綜合渲染呈現出一個 AR 世界。
ARSession 類負責採集現實世界的信息。這一行爲也被稱做__世界追蹤__。它主要的職責是:
它採集到的現實世界信息以 ARFrame 的形式返回。
固然,爲了有一個比較好的追蹤效果,要知足如下要求:
總的說來,就是要提示用戶移動手機,且速度不能太快,要在略微複雜的場景中探測。
ARFrame 包含了兩部分信息:ARAnchor 和 ARCamera。其中,
指的是 ARSession 將如何追蹤世界,有如下幾種子類:
並且,若是要開啓平面檢測,須要加入如下語句:
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
複製代碼
用一段話總結的話,就是 ARSCNView 結合 SCNScene 中的虛擬世界信息和 ARsession 捕捉到的現實世界信息,渲染出 AR 世界。ARConfiguration 指導 ARSession 如何追蹤世界,追蹤的結果以 ARFrame 返回。ARFrame 中的 ANAnchor 信息爲 SceneKit 中的 SCNNode 提供了一些放置的點,以便將虛擬節點和現實錨點綁定。
先介紹 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)
複製代碼
以上方法會在爲一個錨點已經添加、將要更新、已經更新、已經移除一個虛擬錨點時進行回調。
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 的方法中除了錨點之外,還包含節點信息,這可讓咱們有更多的信息進行處理。
下面就幾種經常使用場景給出一些示例代碼。
添加物體能夠有如下兩種方式:自動檢測並添加或者手動點擊添加。
主要利用了 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 類,它能夠檢測用戶手指點擊的地方有沒有通過一些符合要求的點/面,有以下幾種選項:
上面一段代碼的含義是:首先記錄用戶點擊的位置,而後判斷有沒有點擊到特徵點,並將結果按從近到遠的順序返回。若是有最近的一個結果,就生成一個長寬高都爲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 的轉換方法。
有了以上的知識基礎,咱們能夠用如下思路來構建這個項目:
具體的代碼,歡迎參考GitHub。
下面是一些能夠參考的文章/GitHub連接,僅供參考: