https://github.com/rectinajh/ScenkitDemo
Scene Kit是一個(gè)蘋(píng)果Cocoa風(fēng)格的3D渲染框架竞阐,該框架被引入OS X是在WWDC 2012 (那時(shí) OS X 系統(tǒng)還在用喵系命名)。在第一版通用 3D 渲染器發(fā)布后,一年內(nèi)又陸續(xù)增加了像 shader (著色器) 修改器、節(jié)點(diǎn)約束桑涎、骨骼動(dòng)畫(huà)等幾個(gè)強(qiáng)大的特性 (隨 Mavericks 發(fā)布)。 今年(2014年)豫喧,Scene Kit 變的更加強(qiáng)大石洗,支持了粒子效果、物理引擎紧显、腳本事件以及多通道分層渲染等多種技術(shù),以及(也許對(duì)許多人來(lái)說(shuō)最重要的)它被引入iOS缕棵,而蘋(píng)果現(xiàn)在指的是把它當(dāng)成“休閑游戲”的套件孵班。
從一開(kāi)始,我發(fā)現(xiàn)Scene Kit的最大優(yōu)勢(shì)和差異在于與其他圖形框架招驴,如Core Image篙程,Core Animation,Sprite Kit的集成别厘。 這在其他游戲引擎中可不常見(jiàn)虱饿,但是如果你是一個(gè)業(yè)余愛(ài)好者,或者主要是Cocoa 或 Cocoa Touch 框架下的開(kāi)發(fā)者,那么這意味著很多東西應(yīng)該很親切了氮发。
Scene Kit 概要
Scene Kit 構(gòu)建在OpenGL之上渴肉,其中燈光,幾何圖形爽冕,材料和相機(jī)等高級(jí)引擎特性仇祭,這些組件都是面向?qū)ο蟮模憧梢杂檬煜さ?Objective-C 或 Swift 語(yǔ)言來(lái)編寫(xiě)代碼颈畸。 如果用過(guò)最早的版本的OpenGL乌奇,那時(shí)還沒(méi)有 shader,只能苦逼的使用各種底層受限制的 API 開(kāi)發(fā)眯娱。 幸運(yùn)的是 Scene Kit 就好了很多礁苗,高級(jí)配置對(duì)于大多數(shù)常見(jiàn)任務(wù)是足夠的 - 甚至更先進(jìn)的功能,如動(dòng)態(tài)陰影和景深效果徙缴,使用它提供的上層 API 來(lái)配置寂屏,就已經(jīng)足夠了。 不僅如此娜搂,Scene Kit 還允許你直接調(diào)用底層 API迁霎,或自己寫(xiě) shader 進(jìn)行手動(dòng)渲染 (GLSL)。
節(jié)點(diǎn) (Nodes)
除了燈光百宇,幾何圖形考廉,材料和相機(jī)這幾個(gè)具體的對(duì)象之外,Scene Kit還使用節(jié)點(diǎn)層次來(lái)組織其內(nèi)容携御。 每個(gè)節(jié)點(diǎn)相對(duì)于其父節(jié)點(diǎn)具有位置昌粤,旋轉(zhuǎn)和縮放,而父節(jié)點(diǎn)又相對(duì)于其父節(jié)點(diǎn)啄刹,一直向上涮坐,直到根節(jié)點(diǎn)。 假如要給一個(gè)節(jié)點(diǎn)確定一個(gè)位置誓军,就必須將它掛載到節(jié)點(diǎn)樹(shù)中的某個(gè)節(jié)點(diǎn)上袱讹。 使用以下方法管理節(jié)點(diǎn)層次結(jié)構(gòu):
addChildNode(_:)
insertChildNode(_: atIndex:)
removeFromParentNode()
這些方法與 iOS 和 OS X 中管理 view 和 layer 層級(jí)方法如出一轍。
幾何模型對(duì)象
Scene Kit 內(nèi)建了幾種簡(jiǎn)單的幾何模型昵时,如盒子捷雕、球體、平面壹甥、圓錐體等救巷,對(duì)于游戲,一般都會(huì)從文件中加載3D模型句柠。你可以通過(guò)制定文件名來(lái)導(dǎo)入 (或?qū)С? COLLADA 格式的模型文件:
let chessPieces = SCNScene(named: "chess pieces") // SCNScene?
如果一個(gè)從文件里加載的場(chǎng)景可以全部顯示時(shí)浦译,將其設(shè)置成SCNView 的 scene 就好了棒假。 如果場(chǎng)景包含多個(gè)對(duì)象,但是屏幕上只能顯示一些對(duì)象精盅,則可以它們的名稱找到它帽哑,并將其添加到SCNView中呈現(xiàn)的Scene中:
if let knight = chessPieces.rootNode.childNodeWithName("Knight", recursively: true) {
sceneView.scene?.rootNode.addChildNode(knight)
}
這是一個(gè)對(duì)導(dǎo)入文件原始節(jié)點(diǎn)的引用,其中包含了任一和每一個(gè)子節(jié)點(diǎn)渤弛,也包括了模型對(duì)象 (包括其材質(zhì))祝拯,光照,以及綁定在這些節(jié)點(diǎn)上的攝像機(jī)她肯。只要傳入的名字一樣佳头,不論調(diào)用多少次,返回的都是對(duì)同一個(gè)對(duì)象的引用晴氨。
若需要在場(chǎng)景中擁有一個(gè)節(jié)點(diǎn)的多個(gè)拷貝康嘉,如在一個(gè)國(guó)際象棋棋盤(pán)上顯示兩個(gè)馬,你可以對(duì)馬這個(gè)節(jié)點(diǎn)進(jìn)行 copy 或 clone (遞歸的copy)籽前。這將會(huì)拷貝一份節(jié)點(diǎn)的引用亭珍,但兩份引用所指向的材質(zhì)對(duì)象和模型對(duì)象仍然是原來(lái)那個(gè)。所以枝哄,想要單獨(dú)改變副本材質(zhì)的話肄梨,需要再copy一份模型對(duì)象,并對(duì)這個(gè)新的模型對(duì)象設(shè)置新材質(zhì)挠锥。copy一個(gè)模型對(duì)象的速度仍然很快众羡,開(kāi)銷也不高,因?yàn)楦北疽玫捻旤c(diǎn)數(shù)據(jù)還是同一份蓖租。
帶有骨骼動(dòng)畫(huà)的模型對(duì)象也會(huì)擁有一個(gè)皮膚對(duì)象粱侣,它提供了對(duì)骨骼中各個(gè)節(jié)點(diǎn)的訪問(wèn)接口,以及管理骨骼和模型間連接的功能蓖宦。每個(gè)單獨(dú)的骨骼都可以被移動(dòng)和旋轉(zhuǎn)齐婴,而復(fù)雜的動(dòng)畫(huà)需要同時(shí)對(duì)多塊骨骼進(jìn)行操作,如一個(gè)角色走路的動(dòng)畫(huà)稠茂,很可能就是從文件讀取并加到對(duì)象上的 (而不是用代碼一根骨頭一根骨頭的寫(xiě))柠偶。
光照
Scene Kit 中完全都是動(dòng)態(tài)光照,使用起來(lái)一般會(huì)很簡(jiǎn)單主慰,但也意味著與完整的游戲引擎相比嚣州,光照這塊進(jìn)步并不明顯。Scene Kit 提供四種類型的光照:環(huán)境光共螺、定向光源、點(diǎn)光源和聚光燈情竹。
通常來(lái)說(shuō)藐不,旋轉(zhuǎn)坐標(biāo)軸和變換角度并不是設(shè)定光照的最佳方法。下面的例子表示一個(gè)光照對(duì)象通過(guò)一個(gè)節(jié)點(diǎn)對(duì)象來(lái)設(shè)置空間坐標(biāo),再通過(guò) "look at" 約束雏蛮,將光照對(duì)象約束到了目標(biāo)對(duì)象上涎嚼,即使它移動(dòng),光照也會(huì)一直朝向目標(biāo)對(duì)象挑秉。
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]
動(dòng)畫(huà)
Scene Kit 的對(duì)象中絕大多數(shù)屬性都是可以進(jìn)行動(dòng)畫(huà)的法梯,就像 Cocoa (或 Cocoa Touch) 框架一樣,你可以創(chuàng)建一個(gè) CAAnimation 對(duì)象犀概,并指定一個(gè) key path (甚至可以 "position.x") 立哑,然后向一個(gè)對(duì)象施加這個(gè)動(dòng)畫(huà)。同樣的姻灶,你可以在 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 形式的動(dòng)畫(huà) API,你可以創(chuàng)建串行的動(dòng)畫(huà)組产喉,也支持自定義 action 來(lái)協(xié)同使用捂掰。與 Core Animation 不同的是,這些 action 作為游戲循環(huán)的一部分執(zhí)行曾沈,在每一幀都更新模型對(duì)象的值这嚣,而不只是更新表現(xiàn)層的節(jié)點(diǎn)。
假如你之前用過(guò) Sprite Kit塞俱,會(huì)發(fā)現(xiàn) Scene Kit 除了變成了 3D 之外姐帚,沒(méi)有太多陌生的東西。目前敛腌,在 iOS8 (首次支持 Scene Kit) 和 OS X 10.10 下卧土,Scene Kit 和 Sprite Kit 可以協(xié)同工作:對(duì) Sprite Kit 來(lái)說(shuō),3D 模型可以與 2D 精靈混合使用像樊;對(duì) Scene Kit 來(lái)說(shuō)尤莺,Sprite Kit 中的場(chǎng)景和紋理可以作為 Scene Kit 的紋理貼圖,而且 Sprite Kit 的場(chǎng)景可以作為 Scene Kit 場(chǎng)景的蒙層 (如3D游戲中的2D菜單面板生棍,譯者注颤霎。兩套非常像的API和概念 (像場(chǎng)景啊,節(jié)點(diǎn)啊涂滴,約束啊兩邊都有)友酱, 讓人容易混淆。
開(kāi)始用 Scene Kit 寫(xiě)游戲
不僅是動(dòng)作和紋理柔纵,Scene Kit 和 Sprite Kit 還有很多相同之處缔杉。當(dāng)開(kāi)始寫(xiě)游戲的時(shí)候,Scene Kit 和它 2D 版本的小伙伴非常相似搁料,它們的游戲循環(huán)步驟完全一致或详,使用下面幾個(gè)代理回調(diào):
1系羞,更新場(chǎng)景
2,應(yīng)用動(dòng)畫(huà)/動(dòng)作
3霸琴,模擬物理效果
4椒振,應(yīng)用約束
5,渲染
這些回調(diào)在每幀被調(diào)用梧乘,并用來(lái)執(zhí)行游戲相關(guān)的邏輯澎迎,如用戶輸入,AI (人工智能) 和游戲腳本选调。
處理用戶輸入
Scene Kit 與普通 Cocoa 或 Cocoa Touch 應(yīng)用使用一樣的機(jī)制來(lái)處理用戶輸入夹供,如鍵盤(pán)事件、鼠標(biāo)事件学歧、觸摸事件和手勢(shì)識(shí)別罩引,而主要區(qū)別在于 Scene Kit 中只有一個(gè)視圖,場(chǎng)景視圖 (scene view) 枝笨。像鍵盤(pán)事件或如捏取袁铐、滑動(dòng)、旋轉(zhuǎn)的手勢(shì)横浑,只要知道事件的發(fā)生就好了剔桨,但像鼠標(biāo)點(diǎn)擊,或觸碰徙融、拖動(dòng)手勢(shì)等就需要知道具體的事件信息了洒缀。
這些情況下,scene view 可以使用 -hitTest(_: options:) 來(lái)做點(diǎn)擊測(cè)試欺冀。與通常的視圖只返回被點(diǎn)擊的子 view 或子 layer 不同树绩,Scene Kit 返回一個(gè)數(shù)組,里面存有每個(gè)相交的模型對(duì)象以及從攝像機(jī)投向這個(gè)測(cè)試點(diǎn)的射線隐轩。每個(gè) hit test 的結(jié)果包含被擊中模型的節(jié)點(diǎn)對(duì)象饺饭,也包含了交點(diǎn)的詳細(xì)信息 (交點(diǎn)坐標(biāo)、交點(diǎn)表面法線职车,交點(diǎn)的紋理坐標(biāo))瘫俊。多數(shù)情況下,知道第一個(gè)被擊中的節(jié)點(diǎn)就足夠了:
if let firstHit = sceneView.hitTest(tapLocation, options: nil)?.first as? SCNHitTestResult {
let hitNode = firstHit.node
// do something with the node that was hit...
}
擴(kuò)展默認(rèn)渲染流程
光照和材質(zhì)的配置方法很易用悴灵,但有局限性扛芽。假如你有寫(xiě)好的 OpenGL 著色器 (shader),可以用于完全自定制的進(jìn)行材質(zhì)渲染积瞒;如果你只想修改下默認(rèn)的渲染川尖,Scene Kit 暴露了 4 個(gè)入口用于插入 shader代碼 (GLSL) 來(lái)改變默認(rèn)渲染。Scene Kit 在不同入口點(diǎn)分別提供了對(duì)旋轉(zhuǎn)矩陣茫孔、模型數(shù)據(jù)空厌、樣本貼圖及渲染后輸出的色值的訪問(wèn)庐船。
比如银酬,下面的 GLSL 代碼被用在模型數(shù)據(jù)的入口點(diǎn)中嘲更,可以將模型對(duì)象上所有點(diǎn)沿 x 軸扭曲。這是通過(guò)定義一個(gè)函數(shù)來(lái)創(chuàng)建一個(gè)旋轉(zhuǎn)變換揩瞪,并將其應(yīng)用在模型的位置和法線上赋朦。同時(shí),也自定義了一個(gè) "uniform" 變量來(lái)決定對(duì)象該如何被扭曲李破。
// 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) 既可以綁定在模型對(duì)象上宠哄,也可以綁定在它的材質(zhì)對(duì)象上。這兩個(gè)類都完全支持 key-value coding (KVC)嗤攻,你可以指定任意 key 進(jìn)行賦值毛嫉。在 shader 中聲明的 "twistFactor" uniform 變量使得 Scene Kit 在這個(gè)值改變時(shí)自動(dòng)重新綁定 uniform,這使得你也可以用 KVC 來(lái)實(shí)現(xiàn):
torus.setValue(5.0, forKey: "twistFactor")
使用這個(gè) 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")
延時(shí)著色
即使在純 OpenGL 環(huán)境下妇菱,有些圖像效果也無(wú)法通過(guò)一次渲染 pass 完成承粤,我們可以將不同 shader 進(jìn)行序列操作,以達(dá)到后續(xù)處理的目的闯团,稱為延時(shí)著色辛臊。Scene Kit 使用 SCNTechnique 類來(lái)表示這種技術(shù)。它使用字典來(lái)創(chuàng)建房交,字典中定義了繪圖步驟彻舰、輸入輸出、shader 文件候味、符號(hào)等等刃唤。
第一個(gè)渲染 pass 永遠(yuǎn)是 Scene Kit 的默認(rèn)渲染,它輸出場(chǎng)景的顏色和景深白群。如果你不想這時(shí)計(jì)算色值尚胞,可以將材質(zhì)設(shè)置成"恒定"的光照模型,或者將場(chǎng)景里所有光照都設(shè)置成環(huán)境光川抡。
比如辐真,從 Scene Kit 渲染流程的第一個(gè) pass 獲取景深,第二個(gè)獲取法線崖堤,第三個(gè)對(duì)其執(zhí)行邊界檢測(cè)侍咱,你即可以沿輪廓也可以沿邊緣畫(huà)粗線:
參考資料:
https://developer.apple.com/documentation/scenekit
https://developer.apple.com/videos/play/wwdc2014/609/
https://developer.apple.com/videos/play/wwdc2013/500/
https://www.objc.io/issues/18-games/scenekit/