14-《ARKit by Tutorials》讀書筆記1:開始入門

蘋果API搬運工發表於2018-11-16

說明

ARKit系列文章目錄

本文是Ray Wenderlich上《ARKit by Tutorials》的讀書筆記,主要講內容概要和讀後感  

ARKit的主要特點:

  • 追蹤
  • 場景理解
  • 光照估計
  • 場景互動
  • 公制計量單位
  • 渲染整合
    14-《ARKit by Tutorials》讀書筆記1:開始入門

ARKit的主要侷限性:

  • 平面檢測需要花費一定時間
  • 動作處理滯後:不能運動過快
  • 低光照條件
  • 光滑無紋理平面
  • 幽靈穿透效果:不能正確處理遮擋效果

Xcode已經整合了AR專案的建立入口,直接可以建立一個自帶小飛機的AR工程.

14-《ARKit by Tutorials》讀書筆記1:開始入門
14-《ARKit by Tutorials》讀書筆記1:開始入門
14-《ARKit by Tutorials》讀書筆記1:開始入門

專案組織和管理

14-《ARKit by Tutorials》讀書筆記1:開始入門

檔案中的程式碼組織

  • 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中可用的光照模型(著色器)如下:

14-《ARKit by Tutorials》讀書筆記1:開始入門
材質是2D圖片,可以包裹在3D幾何體周圍提供特殊屬性,比如color顏色, specularity高光, reflectivity反射率, shininess發光率, roughness粗糙度, metalness金屬度 甚至transparency透明度.
14-《ARKit by Tutorials》讀書筆記1:開始入門
具體可以看我以前寫的SceneKit系列文章Lights燈光, Materials材質,Shadows陰影

基於物理的渲染(PBR)

PBR光照模型是新引入的特性,可以讓你的3D物體看起來更真實.

下面我們來重點學習一下其中的特性:

Environment map環境貼圖

環境貼圖是一種cube map立方體貼圖,比如天空盒子shybox是這樣的:

14-《ARKit by Tutorials》讀書筆記1:開始入門
SceneKit還支援其它型別的立方體貼圖:
14-《ARKit by Tutorials》讀書筆記1:開始入門

環境貼圖有兩方面作用:一方面類似於reflection map反射貼圖,可以在高反射率表面看到環境影像的反射;另一方面,對於支援PBR的3D物體,可以提供真實的光照環境.如下:

14-《ARKit by Tutorials》讀書筆記1:開始入門

Diffuse map漫反射貼圖

漫反射貼圖提供基礎顏色,無需考慮燈光和其它特效.

14-《ARKit by Tutorials》讀書筆記1:開始入門
需要注意的是,漫反射貼圖可以通過圖片的alpha通道來指定透明度.比如在地球外面再包層大氣雲層圖,你只能看到大氣層的不透明部分.
14-《ARKit by Tutorials》讀書筆記1:開始入門

Normal map法線貼圖

所謂法線,就是垂直於幾何體表面的向量.可以用來計算光線的反射等效果.

法線貼圖通過圖片的RGB通道來定義了畫素級的表面法線.用來與光線混合計算,模擬表面的凹凸效果.這樣無需增加多邊形及頂點資料,就模擬出了真實的表面.

14-《ARKit by Tutorials》讀書筆記1:開始入門

Height map高度貼圖

高度貼圖並不是PBR光照模型的一部分,但是也值得大家學習.高度貼圖是黑白影像,白色代表物體的最高點,黑色代表最低點.

高度貼圖和法線貼圖可以互相轉換,網上有免費工具Normal Map Online — available at bit.ly/1ELCePX

14-《ARKit by Tutorials》讀書筆記1:開始入門
14-《ARKit by Tutorials》讀書筆記1:開始入門

Occlusion map閉塞貼圖

也就是ambient occlusion map環境光閉塞貼圖(OA貼圖).用來阻止環境光照亮閉塞的區域,比如牆壁上的裂縫裡.黑白貼圖,黑色代表不可照亮,白色代表可以照亮.

14-《ARKit by Tutorials》讀書筆記1:開始入門

Emission map發光貼圖/發射貼圖

定義了光照和陰影來製造一種發光效果.例如地球黑夜的燈光(需要關閉光照.PBR下停用環境貼圖):

14-《ARKit by Tutorials》讀書筆記1:開始入門

Self-illumination map自發光貼圖

自發光貼圖在其它所有效果之後才應用;可以用來給最終效果上色,增亮或變暗

14-《ARKit by Tutorials》讀書筆記1:開始入門

Displacement map位移貼圖

在法線貼圖中,我們可以在光滑表面創造出畫素級的不同高度,但它只是幻像,只是改變了光線的反射而已.

在位移貼圖中,我們可以真正地改變表面地形.灰色到白色表示凸起,灰色到黑色表示凹陷:

14-《ARKit by Tutorials》讀書筆記1:開始入門

Metalness and roughness maps金屬度和粗糙度貼圖

PBR的主要特性就是能夠展示出可見的微觀細節,就是用金屬度和粗糙度貼圖來實現的:

14-《ARKit by Tutorials》讀書筆記1:開始入門

  • 金屬度:從後往前,逐漸增強.
  • 粗糙度:從左到右,逐漸增強.

Metalness map金屬度貼圖

金屬度模擬了物體表面的屬性,如反射,折射和菲涅耳反射.該灰度紋理中,黑色代表非金屬,白色代表金屬性表面:

14-《ARKit by Tutorials》讀書筆記1:開始入門

Roughness map粗糙度貼圖

粗糙度貼圖模擬了真實世界表面的微觀細節.產生明亮或暗淡的外觀.該灰度紋理中,黑色代表最粗糙,白色代表最光滑表面:

14-《ARKit by Tutorials》讀書筆記1:開始入門

Detect plane表面檢測

Anchor錨點

錨點是3D物體的參考點,和UIView中的anchor錨點類似.對3D物體應用的transform變換也是相對於錨點的.

14-《ARKit by Tutorials》讀書筆記1:開始入門

新增了新錨點

當平面檢測發現平面時,會新增一個錨點,並建立一個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內建的物體形狀:

14-《ARKit by Tutorials》讀書筆記1:開始入門
並可調節各個引數:
14-《ARKit by Tutorials》讀書筆記1:開始入門

還可以調整整個場景的物理效果速度及物理模擬幀數:

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,這樣就能產生實時的陰影了.
    14-《ARKit by Tutorials》讀書筆記1:開始入門
    14-《ARKit by Tutorials》讀書筆記1:開始入門
    14-《ARKit by Tutorials》讀書筆記1:開始入門

同時文章中還提供了,如何用程式碼來禁止某個物體寫入顏色緩衝區.

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
        }
      }
    }
  }
}
複製程式碼

第一部分讀書筆記結束!

相關文章