鍍金池/ 教程/ iOS/ Scene Kit
與四軸無人機的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學(xué)無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機工作原理
Build 過程

Scene Kit

在 WWDC 2012 (那時 OS X 系統(tǒng)還在用喵系命名),Apple 向 OS X 開發(fā)者們介紹了 Scene Kit,這個 Cocoa 下的 3D 渲染框架。在第一版通用 3D 渲染器發(fā)布后,一年內(nèi)又陸續(xù)增加了像 shader (著色器) 修改器、節(jié)點約束、骨骼動畫等幾個強大的特性 (隨 Mavericks 發(fā)布)。今年,Scene Kit 變的更加強大,支持了粒子效果、物理引擎、腳本事件以及多通道分層渲染等多種技術(shù),而且,對于很多人來說更關(guān)鍵的是,它終于可以在 iOS 中使用了。

一上手,我發(fā)現(xiàn) Scene Kit 最強大和脫穎而出的地方,就是可以與 Core Image,Core Animation,Sprite Kit 等已有的圖形框架相互整合及協(xié)作,這在其他游戲引擎中可不常見,但如果你本身就是個 Cocoa 或 Cocoa Touch 框架下的的開發(fā)者的話,就會感到相當(dāng)親切了。

Scene Kit 概要

Scene Kit 建立在 OpenGL 的基礎(chǔ)上,包含了如光照、模型、材質(zhì)、攝像機等高級引擎特性,這些組件都是面向?qū)ο蟮模憧梢杂檬煜さ?Objective-C 或 Swift 語言來編寫代碼。假如你用過 OpenGL 最早的版本,那時還沒有 shader,只能苦逼的使用各種底層受限制的 API 開發(fā)。而 Scene Kit 就好了很多,對于大多數(shù)需求 (甚至像動態(tài)陰影和景深這種高級特性),使用它提供的上層 API 來配置,就已經(jīng)足夠了。

不僅如此,Scene Kit 還允許你直接調(diào)用底層 API,或自己寫 shader 進行手動渲染 (GLSL)。

節(jié)點 (Nodes)

不僅是光照、模型、材質(zhì)、攝像機這幾個具體的對象,Scene Kit 使用節(jié)點 (在3D圖形學(xué)中,像這樣的樹狀節(jié)點結(jié)構(gòu)一般被稱做 scene graph,這也是 Scene Kit 名稱由來的一種解釋) 以樹狀結(jié)構(gòu)來組織內(nèi)容,每個節(jié)點都存儲了相對其父節(jié)點的位移、旋轉(zhuǎn)角度、縮放等信息,父節(jié)點也是如此,一直向上,直到根節(jié)點。假如要給一個節(jié)點確定一個位置,就必須將它掛載到節(jié)點樹中的某個節(jié)點上,可以使用下面的幾個操作方法:

  • addChildNode(_:)
  • insertChildNode(_: atIndex:)
  • removeFromParentNode()

這些方法與 iOS 和 OS X 中管理 view 和 layer 層級方法如出一轍。

幾何模型對象

Scene Kit 內(nèi)建了幾種簡單的幾何模型,如盒子、球體、平面、圓錐體等,但對于游戲來說,一般都會從文件中加載3D模型。你可以通過制定文件名來導(dǎo)入 (或?qū)С? COLLADA 格式的模型文件:

let chessPieces = SCNScene(named: "chess pieces") // SCNScene?

如果一個從文件里加載的場景可以全部顯示時,將其設(shè)置成 SCNView 的 scene 就好了;但如果加載的場景文件中包含了多個對象,只有一部分對象要顯示在屏幕上時,就可以通過名字找到這個對象,再手動加載到 view 上:

if let knight = chessPieces.rootNode.childNodeWithName("Knight", recursively: true) {
    sceneView.scene?.rootNode.addChildNode(knight)
}

這是一個對導(dǎo)入文件原始節(jié)點的引用,其中包含了任一和每一個子節(jié)點,也包括了模型對象 (包括其材質(zhì)),光照,以及綁定在這些節(jié)點上的攝像機。只要傳入的名字一樣,不論調(diào)用多少次,返回的都是對同一個對象的引用。

http://wiki.jikexueyuan.com/project/objc/images/18-4.png" alt="" />

若需要在場景中擁有一個節(jié)點的多個拷貝,如在一個國際象棋棋盤上顯示兩個馬,你可以對馬這個節(jié)點進行 copyclone (遞歸的copy)。這將會拷貝一份節(jié)點的引用,但兩份引用所指向的材質(zhì)對象和模型對象仍然是原來那個。所以,想要單獨改變副本材質(zhì)的話,需要再copy一份模型對象,并對這個新的模型對象設(shè)置新材質(zhì)。copy一個模型對象的速度仍然很快,開銷也不高,因為副本引用的頂點數(shù)據(jù)還是同一份。

帶有骨骼動畫的模型對象也會擁有一個皮膚對象,它提供了對骨骼中各個節(jié)點的訪問接口,以及管理骨骼和模型間連接的功能。每個單獨的骨骼都可以被移動和旋轉(zhuǎn),而復(fù)雜的動畫需要同時對多塊骨骼進行操作,如一個角色走路的動畫,很可能就是從文件讀取并加到對象上的 (而不是用代碼一根骨頭一根骨頭的寫)。

光照

Scene Kit 中完全都是動態(tài)光照,使用起來一般會很簡單,但也意味著與完整的游戲引擎相比,光照這塊進步并不明顯。Scene Kit 提供四種類型的光照:環(huán)境光、定向光源、點光源和聚光燈。

通常來說,旋轉(zhuǎn)坐標(biāo)軸和變換角度并不是設(shè)定光照的最佳方法。下面的例子表示一個光照對象通過一個節(jié)點對象來設(shè)置空間坐標(biāo),再通過 "look at" 約束,將光照對象約束到了目標(biāo)對象上,即使它移動,光照也會一直朝向目標(biāo)對象。

let spot = SCNLight()
spot.type = SCNLightTypeSpot
spot.castsShadow = true

let spotNode = SCNNode()
spotNode.light = spot
spotNode.position = SCNVector3(x: 4, y: 7, z: 6)

let lookAt = SCNLookAtConstraint(target: knight)
spotNode.constraints = [lookAt]

http://wiki.jikexueyuan.com/project/objc/images/18-5.gif" alt="" />

動畫

Scene Kit 的對象中絕大多數(shù)屬性都是可以進行動畫的,就像 Cocoa (或 Cocoa Touch) 框架一樣,你可以創(chuàng)建一個 CAAnimation 對象,并指定一個 key path (甚至可以 "position.x") ,然后向一個對象施加這個動畫。同樣的,你可以在 SCNTransaction 的 "begin" 和 "commit" 調(diào)用間去改變值,和剛才的 CAAnimation 非常相似:

let move = CABasicAnimation(keyPath: "position.x")
move.byValue  = 10
move.duration = 1.0
knight.addAnimation(move, forKey: "slide right")

Scene Kit 也提供了像 Sprite Kit 那樣的 action 形式的動畫 API,你可以創(chuàng)建串行的動畫組,也支持自定義 action 來協(xié)同使用。與 Core Animation 不同的是,這些 action 作為游戲循環(huán)的一部分執(zhí)行,在每一幀都更新模型對象的值,而不只是更新表現(xiàn)層的節(jié)點。

假如你之前用過 Sprite Kit,會發(fā)現(xiàn) Scene Kit 除了變成了 3D 之外,沒有太多陌生的東西。目前,在 iOS8 (首次支持 Scene Kit) 和 OS X 10.10 下,Scene Kit 和 Sprite Kit 可以協(xié)同工作:對 Sprite Kit 來說,3D 模型可以與 2D 精靈混合使用;對 Scene Kit 來說,Sprite Kit 中的場景和紋理可以作為 Scene Kit 的紋理貼圖,而且 Sprite Kit 的場景可以作為 Scene Kit 場景的蒙層 (如3D游戲中的2D菜單面板,譯者注。兩套非常像的API和概念 (像場景啊,節(jié)點啊,約束啊兩邊都有) 讓人容易混淆。)

開始用 Scene Kit 寫游戲

不僅是動作和紋理,Scene Kit 和 Sprite Kit 還有很多相同之處。當(dāng)開始寫游戲的時候,Scene Kit 和它 2D 版本的小伙伴非常相似,它們的游戲循環(huán)步驟完全一致,使用下面幾個代理回調(diào):

  1. 更新場景
  2. 應(yīng)用動畫/動作
  3. 模擬物理效果
  4. 應(yīng)用約束
  5. 渲染

http://wiki.jikexueyuan.com/project/objc/images/18-6.png" alt="" />

這些回調(diào)在每幀被調(diào)用,并用來執(zhí)行游戲相關(guān)的邏輯,如用戶輸入,AI (人工智能) 和游戲腳本。

處理用戶輸入

Scene Kit 與普通 Cocoa 或 Cocoa Touch 應(yīng)用使用一樣的機制來處理用戶輸入,如鍵盤事件、鼠標(biāo)事件、觸摸事件和手勢識別,而主要區(qū)別在于 Scene Kit 中只有一個視圖,場景視圖 (scene view) 。像鍵盤事件或如捏取、滑動、旋轉(zhuǎn)的手勢,只要知道事件的發(fā)生就好了,但像鼠標(biāo)點擊,或觸碰、拖動手勢等就需要知道具體的事件信息了。

這些情況下,scene view 可以使用 -hitTest(_: options:) 來做點擊測試。與通常的視圖只返回被點擊的子 view 或子 layer 不同,Scene Kit 返回一個數(shù)組,里面存有每個相交的模型對象以及從攝像機投向這個測試點的射線。每個 hit test 的結(jié)果包含被擊中模型的節(jié)點對象,也包含了交點的詳細信息 (交點坐標(biāo)、交點表面法線,交點的紋理坐標(biāo))。多數(shù)情況下,知道第一個被擊中的節(jié)點就足夠了:

if let firstHit = sceneView.hitTest(tapLocation, options: nil)?.first as? SCNHitTestResult  {
    let hitNode = firstHit.node
    // do something with the node that was hit...
}

擴展默認渲染流程

光照和材質(zhì)的配置方法很易用,但有局限性。假如你有寫好的 OpenGL 著色器 (shader),可以用于完全自定制的進行材質(zhì)渲染;如果你只想修改下默認的渲染,Scene Kit 暴露了 4 個入口用于插入 shader代碼 (GLSL) 來改變默認渲染。Scene Kit 在不同入口點分別提供了對旋轉(zhuǎn)矩陣、模型數(shù)據(jù)、樣本貼圖及渲染后輸出的色值的訪問。

比如,下面的 GLSL 代碼被用在模型數(shù)據(jù)的入口點中,可以將模型對象上所有點沿 x 軸扭曲。這是通過定義一個函數(shù)來創(chuàng)建一個旋轉(zhuǎn)變換,并將其應(yīng)用在模型的位置和法線上。同時,也自定義了一個 "uniform" 變量來決定對象該如何被扭曲。

// a function that creates a rotation transform matrix around X
mat4 rotationAroundX(float angle)
{
    return mat4(1.0,    0.0,         0.0,        0.0,
                0.0,    cos(angle), -sin(angle), 0.0,
                0.0,    sin(angle),  cos(angle), 0.0,
                0.0,    0.0,         0.0,        1.0);
}

#pragma body

uniform float twistFactor = 1.0;
float rotationAngle = _geometry.position.x * twistFactor;
mat4 rotationMatrix = rotationAroundX(rotationAngle);

// position is a vec4
_geometry.position *= rotationMatrix;

// normal is a vec3
vec4 twistedNormal = vec4(_geometry.normal, 1.0) * rotationMatrix;
_geometry.normal   = twistedNormal.xyz;

著色修改器 (Shader modifier) 既可以綁定在模型對象上,也可以綁定在它的材質(zhì)對象上。這兩個類都完全支持 key-value coding (KVC),你可以指定任意 key 進行賦值。在 shader 中聲明的 "twistFactor" uniform 變量使得 Scene Kit 在這個值改變時自動重新綁定 uniform,這使得你也可以用 KVC 來實現(xiàn):

torus.setValue(5.0, forKey: "twistFactor")

使用這個 key path 的 CAAnimation 也 ok:

let twist = CABasicAnimation(keyPath: "twistFactor")
twist.fromValue = 5
twist.toValue   = 0
twist.duration  = 2.0

torus.addAnimation(twist, forKey: "Twist the torus")

http://wiki.jikexueyuan.com/project/objc/images/18-7.gif" alt="" />

延時著色

即使在純 OpenGL 環(huán)境下,有些圖像效果也無法通過一次渲染 pass 完成,我們可以將不同 shader 進行序列操作,以達到后續(xù)處理的目的,稱為延時著色。Scene Kit 使用 SCNTechnique 類來表示這種技術(shù)。它使用字典來創(chuàng)建,字典中定義了繪圖步驟、輸入輸出、shader 文件、符號等等。

第一個渲染 pass 永遠是 Scene Kit 的默認渲染,它輸出場景的顏色和景深。如果你不想這時計算色值,可以將材質(zhì)設(shè)置成"恒定"的光照模型,或者將場景里所有光照都設(shè)置成環(huán)境光。

比如,從 Scene Kit 渲染流程的第一個 pass 獲取景深,第二個獲取法線,第三個對其執(zhí)行邊界檢測,你即可以沿輪廓也可以沿邊緣畫粗線:

http://wiki.jikexueyuan.com/project/objc/images/18-8.png" alt="" />


延伸閱讀

如果你想了解更多使用 Scene Kit 做游戲的知識的話,我推薦今年的 WWDC 中的 "Building a Game with Scene Kit" video,并看看 Bananas sample code.

如果你想學(xué)習(xí) Scene Kit 基礎(chǔ)知識,我推薦看這一年的去年的 "What's New in Scene Kit" 相關(guān)視頻。如果你還想學(xué)習(xí)更多,可以關(guān)注我即將發(fā)布的這個主題的書。