14-《ARKit by Tutorials》讀書筆記1:開始入門
- 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透明度.

基於物理的渲染(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 atbit.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 } } } } } 複製程式碼
第一部分讀書筆記結束!