版本記錄
| 版本號 | 時間 |
|---|---|
| V1.0 | 2017.09.27 |
前言
蘋果最近新出的一個API就是ARKit,是在2017年6月6日,蘋果發(fā)布iOS11系統(tǒng)所新增框架,它能夠幫助我們以最簡單快捷的方式實現(xiàn)AR技術(shù)功能。接下來幾篇我們就詳細的對ARKit框架進行詳細的解析。AR相關(guān)代碼已經(jīng)上傳到Github - 刀客傳奇,感興趣的可以看上面幾篇。
1. ARKit框架詳細解析(一)—— 基本概覽
2. ARKit框架詳細解析(二)—— 關(guān)于增強現(xiàn)實和ARKit
3. ARKit框架詳細解析(三)—— 開啟你的第一個AR體驗之旅
Overview - 概覽
同樣是看一下上傳到Github倉庫中的代碼,遵循AR體驗中的視覺反饋,手勢交互和逼真渲染的最佳做法。
增強現(xiàn)實(AR)為用戶提供了與您的應(yīng)用程序中的真實和虛擬3D內(nèi)容進行交互的新方式。 然而,人機接口設(shè)計的許多基本原理仍然有效。 令人信服的AR幻想也需要仔細注意3D資源設(shè)計和渲染。 iOS Human Interface Guidelines 包括有關(guān)AR接口原理的建議。 該項目顯示了應(yīng)用這些準(zhǔn)則的方法,并輕松創(chuàng)建沉浸式,直觀的AR體驗。
此示例應(yīng)用程序提供了簡單的AR體驗,允許用戶將一個或多個逼真的虛擬對象放置在其現(xiàn)實環(huán)境中,然后使用直觀的手勢來排列這些對象。 該應(yīng)用程序提供用戶界面提示,以幫助用戶了解AR體驗的狀態(tài)及其交互選項。
以下部分對應(yīng)于iOS Human Interface Guidelines > Augmented Reality部分,并提供有關(guān)此示例應(yīng)用程序如何實施這些準(zhǔn)則的詳細信息。 有關(guān)每個部分的更詳細的推理,請參閱 iOS Human Interface Guidelines中相應(yīng)的內(nèi)容。
Placing Virtual Objects - 放置虛擬對象
幫助人們了解何時找到一個表面并放置一個對象。 FocusSquare類在AR視圖中繪制一個方形輪廓,為用戶提供關(guān)于ARKit世界跟蹤狀態(tài)的提示。

該方塊改變大小和方向以反映估計的場景深度,并用突出的動畫切換打開和關(guān)閉狀態(tài),以指示ARKit是否檢測到適合放置對象的平面。 在用戶放置虛擬對象后,焦點方格消失,保持隱藏,直到用戶將相機指向另一個表面。
當(dāng)用戶放置對象時,適當(dāng)?shù)仨憫?yīng)。 當(dāng)用戶選擇要放置的虛擬對象時,示例應(yīng)用程序的setPosition(_:relativeTo:smoothMovement)方法使用FocusSquare對象的簡單啟發(fā)式方法將對象放置在屏幕中間大致逼真的位置,即使ARKit尚未 在該地點偵測到一個平面。
下面看一下代碼示例
guard let cameraTransform = session.currentFrame?.camera.transform,
let focusSquarePosition = focusSquare.lastPosition else {
statusViewController.showMessage("CANNOT PLACE OBJECT\nTry moving left or right.")
return
}
virtualObjectInteraction.selectedObject = virtualObject
virtualObject.setPosition(focusSquarePosition, relativeTo: cameraTransform, smoothMovement: false)
這個位置可能不是用戶想要放置虛擬對象的真實表面的準(zhǔn)確估計,但是它足夠接近以快速獲得對象。
隨著時間的推移,ARKit會檢測到平面并調(diào)整其位置的估計,調(diào)用渲染器: renderer:didAddNode:forAnchor: 和 renderer:didUpdateNode:forAnchor:代理方法來報告結(jié)果。 在這些方法中,示例應(yīng)用程序調(diào)用其adjustOntoPlaneAnchor(_:using :)方法來確定先前放置的虛擬對象是否接近檢測到的平面。 如果是這樣,該方法使用微妙的動畫將虛擬對象移動到平面上,從而使對象看起來處于用戶選擇的位置,同時受益于ARKit對該位置的真實表面的精確估計:
// Move onto the plane if it is near it (within 5 centimeters).
let verticalAllowance: Float = 0.05
let epsilon: Float = 0.001 // Do not update if the difference is less than 1 mm.
let distanceToPlane = abs(planePosition.y)
if distanceToPlane > epsilon && distanceToPlane < verticalAllowance {
SCNTransaction.begin()
SCNTransaction.animationDuration = CFTimeInterval(distanceToPlane * 500) // Move 2 mm per second.
SCNTransaction.animationTimingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
position.y = anchor.transform.columns.3.y
SCNTransaction.commit()
}
User Interaction with Virtual Objects - 用戶與虛擬對象的交互
- 允許人們使用標(biāo)準(zhǔn)熟悉的手勢直接與虛擬對象進行交互。
示例應(yīng)用程序使用單指點擊,單指和雙指平移以及雙指旋轉(zhuǎn)手勢識別器來讓用戶定位和定向虛擬對象。 示例代碼的VirtualObjectInteraction類管理這些手勢。
- 一般來說,保持互動簡單。
當(dāng)拖動虛擬對象(請參閱translate(_:basedOn:infinitePlane :)方法)時,示例應(yīng)用程序?qū)ο蟮囊苿酉拗茷榉胖迷谄渖系亩S平面。 類似地,由于虛擬對象依賴于水平平面,旋轉(zhuǎn)手勢(請參閱didRotate(_ :)方法)僅繞其垂直軸旋轉(zhuǎn)對象,以使對象保留在平面上。
- 在交互式虛擬物體的合理接近范圍內(nèi)回應(yīng)手勢。
示例代碼的objectInteracting(with : in :)方法使用手勢識別器提供的觸摸位置來執(zhí)行命中測試。 通過對虛擬對象的邊界框進行打擊測試,該方法使得用戶觸摸更可能影響對象,即使觸摸位置不在對象具有可見內(nèi)容的點上。 通過對多點觸控手勢執(zhí)行多次命中測試,該方法使得用戶觸摸更可能影響預(yù)期對象:
for index in 0..<gesture.numberOfTouches {
let touchLocation = gesture.location(ofTouch: index, in: view)
// Look for an object directly under the `touchLocation`.
if let object = sceneView.virtualObject(at: touchLocation) {
return object
}
}
// As a last resort look for an object under the center of the touches.
return sceneView.virtualObject(at: gesture.center(in: view))
- 考慮用戶啟動的對象縮放是否必要。
這個AR經(jīng)驗會放置可能自然地出現(xiàn)在用戶環(huán)境中的現(xiàn)實虛擬對象,因此保留對象的內(nèi)在大小有助于實現(xiàn)現(xiàn)實。 因此,示例應(yīng)用程序不會添加手勢或其他UI來啟用對象縮放。 另外,通過不包括縮放手勢,示例應(yīng)用程序防止用戶對于手勢是調(diào)整對象大小還是改變對象距離相機的距離而感到困惑。 (如果您選擇在應(yīng)用程序中啟用對象縮放,請使用捏捏手勢識別器。)
- 警惕潛在的沖突手勢。
示例代碼的ThresholdPanGesture類是一個UIPanGestureRecognizer子類,它提供一種延遲手勢識別器效果的方法,直到正在進行的手勢通過指定的移動閾值。 示例代碼的touchesMoved(with :)方法使用此類讓用戶在拖動對象之間平滑過渡并在單個雙指手勢中旋轉(zhuǎn)對象:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
let translationMagnitude = translation(in: view).length
// Adjust the threshold based on the number of touches being used.
let threshold = ThresholdPanGesture.threshold(forTouchCount: touches.count)
if !isThresholdExceeded && translationMagnitude > threshold {
isThresholdExceeded = true
setTranslation(.zero, in: view)
}
}
- 確保虛擬對象的移動平滑。
示例代碼的setPosition(_:relativeTo:smoothMovement)方法在導(dǎo)致拖動對象的觸摸手勢位置和該對象的最近位置的歷史記錄之間插值。 通過根據(jù)距離攝像機的距離平均最近的位置,該方法可以產(chǎn)生平滑的拖動運動,而不會使拖動的對象滯后于用戶的手勢:
if smoothMovement {
let hitTestResultDistance = simd_length(positionOffsetFromCamera)
// Add the latest position and keep up to 10 recent distances to smooth with.
recentVirtualObjectDistances.append(hitTestResultDistance)
recentVirtualObjectDistances = Array(recentVirtualObjectDistances.suffix(10))
let averageDistance = recentVirtualObjectDistances.average!
let averagedDistancePosition = simd_normalize(positionOffsetFromCamera) * averageDistance
simdPosition = cameraWorldPosition + averagedDistancePosition
} else {
simdPosition = cameraWorldPosition + positionOffsetFromCamera
}
- 探索更有吸引力的互動方式。
在AR體驗中,平移手勢 - 即將手指移動到設(shè)備的屏幕上 - 并不是將虛擬內(nèi)容拖到新位置的唯一自然方法。 用戶還可以直觀地嘗試在移動設(shè)備的同時將手指保持在屏幕上,有效地將觸摸點拖過AR場景。
示例應(yīng)用程序通過在拖動手勢正在進行時持續(xù)調(diào)用其updateObjectToCurrentTrackingPosition()方法來支持這種手勢,即使手勢的觸摸位置沒有改變。 如果設(shè)備在拖動期間移動,則該方法計算與觸摸位置相對應(yīng)的新世界位置,并相應(yīng)地移動虛擬對象。
Entering Augmented Reality - 進入增強現(xiàn)實
指示初始化發(fā)生時涉及用戶。 示例應(yīng)用程序顯示有關(guān)AR會話狀態(tài)的文本提示以及使用浮動文本視圖與AR體驗進行交互的說明。 示例代碼的StatusViewController類管理此視圖,顯示允許用戶讀取它們之后淡出的臨時指令,或重要的狀態(tài)消息,直到用戶更正問題為止。

處理問題 - Handling Problems
- 如果不符合他們的期望,允許人們重置體驗。
示例應(yīng)用程序具有始終在UI右上角可見的重置按鈕,允許用戶重新啟動AR體驗,無論其當(dāng)前狀態(tài)如何。 請參閱示例代碼中的restartExperience()方法。
僅在有能力的設(shè)備上提供AR功能。 示例應(yīng)用程序需要ARKit的核心功能,因此它在Info.plist文件的一部分中定義了arkit密鑰。 部署內(nèi)置項目時,此鍵可防止在不支持ARKit的設(shè)備上安裝應(yīng)用程序。
如果您的應(yīng)用程序使用AR作為輔助功能,請使用該方法來確定是否隱藏需要ARKit的功能。
后記
未完,待續(xù)~~~
