概覽
什么是 AR摄乒?它不同于 VR悠反,VR 是一種沉浸式體驗(yàn),你眼前的一切都是虛擬的馍佑,根本不需要理會(huì)現(xiàn)實(shí)世界(當(dāng)然斋否,如果體驗(yàn) VR 的地方很小,你可能還需要注意一下走位拭荤,免得發(fā)生意外)茵臭,VR 是 Virtual Reality,也就是虛擬現(xiàn)實(shí)舅世,而 AR 是 Augmented Reality旦委,增強(qiáng)現(xiàn)實(shí)。AR 是對(duì)現(xiàn)實(shí)世界的一種補(bǔ)充雏亚,一種增強(qiáng)缨硝,你的大部分精力還是放在現(xiàn)實(shí)世界中。AR 被用來豐富化現(xiàn)實(shí)世界的圖像罢低,例如在一張什么都沒有的桌子上顯示出一個(gè)城堡追葡,又或者在一面干干凈凈的墻面上繪制涂鴉或擺放一面電視,但不同于普通的 PS 照片,AR 可以是動(dòng)態(tài)的宜肉,通過 AR 引擎創(chuàng)造出來的虛擬物體會(huì)根據(jù)人物的移動(dòng)而自動(dòng)調(diào)整位置匀钧,也就是說你從桌子的這個(gè)角走到另一個(gè)角,城堡依然在桌子上谬返,位置不變之斯,角度不變,甚至光照也不變遣铝,就好似真的有一座城堡在桌子上佑刷。
AR 和 VR 都是創(chuàng)造虛擬圖像的技術(shù),但前者是基于現(xiàn)實(shí)世界的酿炸,沒有現(xiàn)實(shí)世界的圖像瘫絮,AR 創(chuàng)造出來的圖像是沒什么意義的,而 VR 是完全虛擬的填硕,它會(huì)在虛擬世界中虛擬出一面桌子麦萤,然后在虛擬的桌子上在創(chuàng)造一個(gè)虛擬的城堡。
對(duì)于 AR 這樣一個(gè)神奇的技術(shù)扁眯,Apple 在 WWDC 2017 上順勢(shì)推出了 AR 引擎和開發(fā)框架壮莹,這個(gè)框架就叫做 ARKit
Apple 的 ARKit 可以將你創(chuàng)造的 2D 平面對(duì)象和 3D 立體對(duì)象,通過 iOS 設(shè)備的攝像頭映射到現(xiàn)實(shí)世界中姻檀,透過屏幕你就能在當(dāng)前周圍的環(huán)境里看到你創(chuàng)造的對(duì)象 命满。
你可以通過 Apple 的 Metal、SpriteKit 和 SceneKit 來創(chuàng)造你的精靈绣版,你也可以通過強(qiáng)大的第三方引擎例如 Unity胶台、Unreal Engine 為你的 AR app 創(chuàng)造模型。
ARKit 基于 Visual Inertial Odometry (VIO) 技術(shù)實(shí)現(xiàn)杂抽,利用 iPhone 和 iPad 的各種傳感器提供的數(shù)據(jù)來計(jì)算你的當(dāng)前位置概作,同時(shí)更新虛擬物體的相對(duì)位置,還可以尋找到相對(duì)水平的平面默怨,例如桌子讯榕。所以這需要龐大的計(jì)算力,Apple 為此也做了一定限制:
- 如果要使用 worldTeacking匙睹,你的設(shè)備所使用的處理器必須大于 A9 處理器
下面就讓我們一起來體驗(yàn)一下這神奇的框架吧愚屁!
使用 ARKit
在一切開始前,你需要打開最新的 Xcode 9.0 beta 版痕檬,然后新建一個(gè)工程:
1.新建一個(gè) Augmented Reality App
2.填寫 App 的 Bundle 名霎槐,在 Content Technology 一欄選擇 SceneKit,這表示我們將使用 SceneKit 來創(chuàng)造 AR 環(huán)境
3.選擇保存位置梦谜,一個(gè) AR 工程就新建完畢了
新建好以后丘跌,你會(huì)看到工程里已經(jīng)有一些文件袭景,而且和以往一樣,你可以直接運(yùn)行這個(gè)工程到你的設(shè)備上闭树,按下 Command + R 運(yùn)行在 iPhone 上試試吧耸棒!
在調(diào)試 AR app 的時(shí)候,你可能需要到處走動(dòng)报辱,牽著一根線可能會(huì)大大阻礙你的行動(dòng)与殃,幸運(yùn)的是 Xcode 9.0 開始支持了無線開發(fā),你可以以無線的方式將 App 安裝在設(shè)備上碍现,并且還能正常接收調(diào)試數(shù)據(jù)幅疼,具體怎么做請(qǐng)看我的探索 Xcode 9
Demo 工程是讓一艘飛船懸停在空中,如果你已經(jīng)運(yùn)行過這個(gè) Demo 了應(yīng)該會(huì)覺得效果還蠻不錯(cuò)昼接,雖然某些 AR 技術(shù)的通病 Apple 還沒有解決爽篷,讓我們先來看一看這個(gè)工程里相對(duì)重要的文件有哪些吧:
art.scnassets:這里面存放的就是場(chǎng)景文件,一個(gè)場(chǎng)景里可以有多個(gè)物體
ship.scn:我們的飛船就來自這個(gè)場(chǎng)景文件慢睡,你可以打開并編輯它
texture.png:這是一個(gè)紋理球文件逐工,每一個(gè) 3D 模型都應(yīng)該需要紋理,否則看上去會(huì)很單調(diào)一睁,除非你刻意創(chuàng)造那種場(chǎng)景钻弄。而這個(gè)紋理就是這艘飛船的紋理佃却。
ViewController.swift:默認(rèn)的視圖控制器者吁,它繼承于 UIViewController 并遵守 ARSCNViewDelegate 協(xié)議。
Main.storyboard:Storyboard 文件饲帅,里面有一些默認(rèn)的組件已經(jīng)被添加好了
剩下的一些資源文件和普通的 Cocoa touch 工程沒什么區(qū)別复凳,相信大家應(yīng)該都了解了。
那么讓我們繼續(xù)來看大家最關(guān)心的部分灶泵,ViewController 這個(gè)文件展示了顯示一個(gè)虛擬物體在現(xiàn)實(shí)世界中的最基本的步驟和內(nèi)容育八,相當(dāng)有指導(dǎo)意義,我們以方法和屬性區(qū)分赦邻,分別解釋髓棋。
默認(rèn)的 ViewController
@IBOutlet var sceneView: ARSCNView!
這就是 Xcode 幫我們創(chuàng)建好的一個(gè) ARSceneView 對(duì)象,而且做好了牽線工作惶洲。這個(gè) Scene 和普通的 SceneKit 的 Scene 不同的是按声,它是 AR Scene,專門顯示 AR 的
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate 設(shè)置 view 的代理
sceneView.delegate = self
// Show statistics such as fps and timing information 顯示調(diào)試信息
sceneView.showsStatistics = true
// Create a new scene 創(chuàng)造一個(gè)新的場(chǎng)景
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// Set the scene to the view 使默認(rèn)場(chǎng)景為我們新創(chuàng)建的場(chǎng)景
sceneView.scene = scene
}
在 viewDidLoad 中恬吕,我們可以為創(chuàng)建好的 sceneView 設(shè)置代理签则,showStatistics 屬性可以指定是否顯示調(diào)試信息,這些信息包含 fps 值铐料、時(shí)間信息等渐裂。
我們之前在資源文件里看到了豺旬,有一個(gè)飛船的場(chǎng)景文件,這個(gè)場(chǎng)景文件也是在這里被載入的柒凉,使用 SCNScene 的初始化方法 init(named:)
來載入一個(gè)場(chǎng)景文件族阅,最后我們將 sceneView 的默認(rèn)場(chǎng)景替換成我們載入的場(chǎng)景。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingSessionConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
每一個(gè) AR 場(chǎng)景被創(chuàng)建時(shí)扛拨,它都需要一個(gè)配置類來告訴它應(yīng)該使用怎樣的方式去運(yùn)行耘分,配置類一共有兩種:
ARWorldTrackingSessionConfiguration
ARSessionConfiguration
這里使用的就是第一種,兩者的區(qū)別在于物體的擺放方式:
- 設(shè)置為 ARWorldTrackingSessionConfiguration 時(shí)绑警,物體放在基于現(xiàn)實(shí)世界的坐標(biāo)系求泰,你的攝像頭只是這個(gè)坐標(biāo)系的另一個(gè)點(diǎn),移動(dòng)設(shè)備的時(shí)候物體是相對(duì)坐標(biāo)系靜止的计盒。
- 設(shè)置為 ARSessionConfiguration 時(shí)渴频,坐標(biāo)系以你的設(shè)備為基準(zhǔn),你的設(shè)備平移時(shí)北启,整個(gè)坐標(biāo)系也跟著平移卜朗,坐標(biāo)系上的物體也跟著平移,所以攝像頭和物體是相對(duì)靜止的咕村,只有設(shè)備橫向旋轉(zhuǎn)時(shí)能觀察坐標(biāo)系的其他部分的元素场钉。
在創(chuàng)建好配置類的對(duì)象后,你就可以使用 sceneView 的 session 來啟動(dòng)一次 AR session懈涛,這里的 session 就是每一個(gè) AR 進(jìn)程所需要的 session 對(duì)話逛万,你可以使用 run() 方法來啟動(dòng)一個(gè) session。
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
當(dāng)你不希望 session 繼續(xù)占用系統(tǒng)資源時(shí)批钠,你可以使用 pause() 來停止一次 session宇植,你可以再一次使用 run 來啟動(dòng)新的 session。
另外埋心,你還可以在 session 執(zhí)行的過程中動(dòng)態(tài)更新配置類指郁。
// Override to create and configure nodes for anchors added to the view's session.
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
return node
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
}
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
這里就列出了一些 ARSCNViewDelegate 中的代理方法,其中:
renderer(:nodeFor:):新建一個(gè) Anchor(錨點(diǎn))時(shí)會(huì)調(diào)用
renderer(:updateAtTime:):更新場(chǎng)景時(shí)會(huì)調(diào)用
session(:didFailWithError:):出現(xiàn)錯(cuò)誤時(shí)會(huì)調(diào)用拷呆,此時(shí)會(huì)終止 session
sessionWasInterrupted(:):中斷 session 時(shí)會(huì)調(diào)用
sessionInterruptionEnded(:):中斷結(jié)束時(shí)會(huì)調(diào)用
到這里大家都應(yīng)該知道一個(gè)場(chǎng)景的基本構(gòu)造闲坎,以及如何載入一個(gè)場(chǎng)景。
添加一張“照片”到 AR 場(chǎng)景中
大家應(yīng)該都發(fā)現(xiàn)了茬斧,我們是利用 SceneKit 創(chuàng)建的 AR 場(chǎng)景腰懂,所以我們可以將一部分 SceneKit 的東西拿來用,使用 SceneKit 創(chuàng)造的三維模型將其載入到 AR 世界里去啥供,事實(shí)上悯恍,我們的 scn 場(chǎng)景文件本身也就是 SceneKit 的概念。
在這里我們將利用 SCNPlane 類來創(chuàng)造一個(gè)平面伙狐,然后設(shè)置這個(gè)平面的紋理為一張照片涮毫,在我們點(diǎn)擊屏幕時(shí)將這個(gè)對(duì)象添加到 AR 場(chǎng)景中瞬欧。
1.在 touchesBegan 中實(shí)現(xiàn)
既然是點(diǎn)擊屏幕添加物體,那么就可以在 touchesBegan 中完成
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// code here
}
2.獲取當(dāng)前幀
對(duì)于 AR 場(chǎng)景罢防,當(dāng)前幀不僅僅包含了幀信息和圖像信息艘虎,還包含了當(dāng)前 ARCamera 的坐標(biāo)信息、sceneView 的大小等等咒吐,我們要實(shí)現(xiàn)在屏幕前添加一個(gè)物體野建,那么就得獲取當(dāng)前 Camera 的位置,否則我們添加的物體可能在任何一個(gè)地方恬叹,甚至你找不到的地方
// 從 session 中獲取 currentFrame
guard let currentFrame = sceneView.session.currentFrame else { return }
3.創(chuàng)建一個(gè) Node
要?jiǎng)?chuàng)建一個(gè) Node候生,你需要一個(gè)繼承自 SCNGeometry 的對(duì)象,這里我們使用的 SCNPlane 就繼承自 SCNGeometry
// 創(chuàng)建一個(gè) plane绽昼。并設(shè)置大小唯鸭,這里的大小單位接近米,不能太大硅确,否則最后會(huì)擋住我們的視線目溉,看不出效果
let plane = SCNPlane(width: 0.6, height: 0.4)
// Meterial 就是我們的材料,也就是紋理菱农,我們?cè)O(shè)置紋理的內(nèi)容為一個(gè) UIImage 對(duì)象
plane.firstMaterial?.diffuse.contents = UIImage(named: "material.jpg")
plane.firstMaterial?.lightingModel = .constant
// 利用創(chuàng)建好的 plane 來創(chuàng)建一個(gè)精靈(Node)
let planeNode = SCNNode(geometry: plane)
// 最后把精靈加入到 sceneView 的根節(jié)點(diǎn)
sceneView.scene.rootNode.addChildNode(planeNode)
每一個(gè) scene 都有一個(gè)根節(jié)點(diǎn)缭付,它類似于一個(gè) scene 的坐標(biāo)中心,所有子節(jié)點(diǎn)都依附于根節(jié)點(diǎn)循未,根節(jié)點(diǎn)產(chǎn)生的位移陷猫、旋轉(zhuǎn)變化都會(huì)應(yīng)用于子節(jié)點(diǎn)。
4.設(shè)置新節(jié)點(diǎn)的位置
我們說只厘,需要把新加入的物體放在我們面前烙丛,那就必須要指定好它的詳細(xì)位置舅巷,這時(shí)候我們最開始獲取的 currentFrame 就有用了
// 熟悉 SceneKit 的朋友應(yīng)該都了解這里的操作羔味,這里的重點(diǎn)是利用 currentFrame 的 camera 屬性來獲取 transform
// 這個(gè) transform 的各種位置屬性就是當(dāng)前攝像機(jī)所處的位置信息,所以我們可以直接利用它
var translation = matrix_identity_float4x4
translation.columns.3.z = -20
let transform = matrix_multiply(currentFrame.camera.transform, translation)
// 最后把 transform 傳給 node 的 simdTransform
planeNode.simdTransform = transform
5.運(yùn)行钠右,測(cè)試
現(xiàn)在重新編譯工程赋元,并運(yùn)行在設(shè)備上,每當(dāng)我們點(diǎn)擊一下屏幕時(shí)飒房,面前都會(huì)有一張我們的照片出現(xiàn)搁凸,而且此時(shí)你可以移動(dòng)相機(jī),在其他地方繼續(xù)添加照片狠毯,最后可以創(chuàng)造出一個(gè)很帥氣的場(chǎng)景~