說明
本文是Ray Wenderlich上《ARKit by Tutorials》的讀書筆記,主要講內容概要和讀後感
ARKit的主要特點:
- 追蹤
- 場景理解
- 光照估計
- 場景互動
- 公制計量單位
- 渲染整合
ARKit的主要侷限性:
- 平面檢測需要花費一定時間
- 動作處理滯後:不能運動過快
- 低光照條件
- 光滑無紋理平面
- 幽靈穿透效果:不能正確處理遮擋效果
Xcode已經整合了AR專案的建立入口,直接可以建立一個自帶小飛機的AR工程.
專案組織和管理
檔案中的程式碼組織
- Properties: 類的屬性.
- Outlets: xib上的元素.
- Actions: 執行的動作.
- View Management: 檢視生命週期等方法.
- Initialization: 初始化方法.
- ARSCNViewDelegate 協議extension:AR代理方法
session控制
- Pausing:ARSession.pause()可以用來暫停會話.
- Resuming:ARSession.run()可恢復一個暫停的session.
- Updating:ARSession.run(ARSessionConfig)可以更新配置項.
- Resetting: ARSession.run(_:options:)可以重置session. 當session狀態改變時,可以在代理方法中處理:
func session(_ session: ARSession,
cameraDidChangeTrackingState camera: ARCamera) {
switch camera.trackingState {
// 1
case .notAvailable:
trackingStatus = "Tracking: Not available!"
// 2
case .normal:
trackingStatus = "Tracking: All good!"
// 3
case .limited(let reason):
switch reason {
case .excessiveMotion:
trackingStatus = "Tracking: Limited due to
excessive motion!"
// 3.1
case .insufficientFeatures:
trackingStatus = "Tracking: Limited due to
insufficient features!"
// 3.2
case .initializing:
trackingStatus = "Tracking: Initializing..."
// 3.3
case .relocalizing:
trackingStatus = "Tracking: Relocalizing..."
}
}
}
}
複製程式碼
除錯選項
可以開啟AR檢視的debug選項來幫助除錯:
sceneView.debugOptions = []
複製程式碼
可配置項如下:
- Feature points:顯示檢測到的特徵點.
- World origin:紅綠藍三色線交叉組成的世界座標原點.
- Bounding boxes:3D物體的邊界盒.
- Wireframe:3D物體線框圖.
著色器,材質和紋理
在SceneKit中可用的光照模型(著色器)如下:
材質是2D圖片,可以包裹在3D幾何體周圍提供特殊屬性,比如color顏色, specularity高光, reflectivity反射率, shininess發光率, roughness粗糙度, metalness金屬度 甚至transparency透明度. 具體可以看我以前寫的SceneKit系列文章Lights燈光, Materials材質,Shadows陰影基於物理的渲染(PBR)
PBR光照模型是新引入的特性,可以讓你的3D物體看起來更真實.
下面我們來重點學習一下其中的特性:
Environment map環境貼圖
環境貼圖是一種cube map立方體貼圖,比如天空盒子shybox是這樣的:
SceneKit還支援其它型別的立方體貼圖:環境貼圖有兩方面作用:一方面類似於reflection map反射貼圖,可以在高反射率表面看到環境影象的反射;另一方面,對於支援PBR的3D物體,可以提供真實的光照環境.如下:
Diffuse map漫反射貼圖
漫反射貼圖提供基礎顏色,無需考慮燈光和其它特效.
需要注意的是,漫反射貼圖可以通過圖片的alpha通道來指定透明度.比如在地球外面再包層大氣雲層圖,你只能看到大氣層的不透明部分.Normal map法線貼圖
所謂法線,就是垂直於幾何體表面的向量.可以用來計算光線的反射等效果.
法線貼圖通過圖片的RGB通道來定義了畫素級的表面法線.用來與光線混合計算,模擬表面的凹凸效果.這樣無需增加多邊形及頂點資料,就模擬出了真實的表面.
Height map高度貼圖
高度貼圖並不是PBR光照模型的一部分,但是也值得大家學習.高度貼圖是黑白影象,白色代表物體的最高點,黑色代表最低點.
高度貼圖和法線貼圖可以互相轉換,網上有免費工具Normal Map Online — available at bit.ly/1ELCePX
Occlusion map閉塞貼圖
也就是ambient occlusion map環境光閉塞貼圖(OA貼圖).用來阻止環境光照亮閉塞的區域,比如牆壁上的裂縫裡.黑白貼圖,黑色代表不可照亮,白色代表可以照亮.
Emission map發光貼圖/發射貼圖
定義了光照和陰影來製造一種發光效果.例如地球黑夜的燈光(需要關閉光照.PBR下停用環境貼圖):
Self-illumination map自發光貼圖
自發光貼圖在其它所有效果之後才應用;可以用來給最終效果上色,增亮或變暗
Displacement map位移貼圖
在法線貼圖中,我們可以在光滑表面創造出畫素級的不同高度,但它只是幻像,只是改變了光線的反射而已.
在位移貼圖中,我們可以真正地改變表面地形.灰色到白色表示凸起,灰色到黑色表示凹陷:
Metalness and roughness maps金屬度和粗糙度貼圖
PBR的主要特性就是能夠展示出可見的微觀細節,就是用金屬度和粗糙度貼圖來實現的:
- 金屬度:從後往前,逐漸增強.
- 粗糙度:從左到右,逐漸增強.
Metalness map金屬度貼圖
金屬度模擬了物體表面的屬性,如反射,折射和菲涅耳反射.該灰度紋理中,黑色代表非金屬,白色代表金屬性表面:
Roughness map粗糙度貼圖
粗糙度貼圖模擬了真實世界表面的微觀細節.產生明亮或暗淡的外觀.該灰度紋理中,黑色代表最粗糙,白色代表最光滑表面:
Detect plane表面檢測
Anchor錨點
錨點是3D物體的參考點,和UIView中的anchor錨點類似.對3D物體應用的transform變換也是相對於錨點的.
新增了新錨點
當平面檢測發現平面時,會新增一個錨點,並建立一個SCNNode,並呼叫代理方法:
// 1
func renderer(_ renderer: SCNSceneRenderer,
didAdd node: SCNNode, for anchor: ARAnchor) {
// 2
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// 3
DispatchQueue.main.async {
// 4
let planeNode = self.createARPlaneNode(
planeAnchor: planeAnchor,
color: UIColor.yellow.withAlphaComponent(0.5))
// 5
node.addChildNode(planeNode)
}
}
複製程式碼
錨點更新
當錨點更新時,也會呼叫代理方法:
// 1
func renderer(_ renderer: SCNSceneRenderer,
didUpdate node: SCNNode, for anchor: ARAnchor) {
// 2
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// 3
DispatchQueue.main.async {
// 4
self.updateARPlaneNode(planeNode: node.childNodes[0],
planeAchor: planeAnchor)
}
}
複製程式碼
Physics物理效果
Physics body物理形體
首先要了解的是就是physics body物理形體的概念:
- static body:靜態形體,在物理模擬中可以與其它物體發生作用,但自身不受影響,始終在原來位置上.如牆壁.
- dynamic body:動態形體,在物理模擬中完全由物理引擎控制並可以與其它物理形體發生作用.如小球.
- kinematic body:動力學形體,在物理模擬中不受物理引擎控制,但可以通過程式碼來移動.如電梯.
Physics body type物理形體的形狀
還有SceneKit內建的物體形狀:
並可調節各個引數:還可以調整整個場景的物理效果速度及物理模擬幀數:
scene.physicsWorld.speed = 0.05 //效果就像慢鏡頭
scene.physicsWorld.timeStep = 1.0 / 60.0 //每秒60幀;如果物體運動速度過快,需要增加幀數以提高精度,但也會提高CPU的負載.
複製程式碼
Force力
力使用3維向量SCNVector3表示,使用applyForce(_: atPosition: impluse:)方法來新增一個力,並指定位置.一個力可以同時影響線速度和角速度. impluse脈衝狀只作用一次,比如踢一個球,非脈衝狀的則可以持續作用. Position位置可以影響力的作用效果
更多物理效果相關內容,可以參考physics物理效果
燈光和陰影
在AR中給物體新增陰影一般有兩種方法:
- 在物體下面放上一塊淺灰紋理的平面,這樣彷彿就有了陰影.這也就是所謂的將光照和陰影"烘焙"進紋理中.
- 在物體下面放一塊平面,並將平面的Reflectivity設定為0;再新增一個光源,並將光源的Mode改為Deferred,這樣就能產生實時的陰影了.
同時文章中還提供了,如何用程式碼來禁止某個物體寫入顏色緩衝區.
func hideARPlaneNodes() {
// 1
for anchor in
(self.sceneView.session.currentFrame?.anchors)! {
// 2
if let node = self.sceneView.node(for: anchor) {
// 3
for child in node.childNodes {
// 4
let material = child.geometry?.materials.first!
material?.colorBufferWriteMask = []
}
}
}
}
複製程式碼
更多相關內容可以看我以前寫的SceneKit系列文章Lights燈光, Shadows陰影以及官方Demo解讀中關於陰影的官方解讀蘋果官方AR變色龍Demo解讀
Hit testing命中測試
命中測試可以用來提供與3D物體的互動
override func touchesBegan(_ touches: Set<UITouch>,
with event: UIEvent?) {
DispatchQueue.main.async {
// 1
if let touchLocation = touches.first?.location(
in: self.sceneView) {
// 2
if let hit = self.sceneView.hitTest(touchLocation,
options: nil).first {
// 3
if hit.node.name == "dice" {
// 4
hit.node.removeFromParentNode()
self.diceCount += 1
}
}
}
}
}
複製程式碼
第一部分讀書筆記結束!