轉(zhuǎn)載請注明原作者
上篇文章我們介紹如何創(chuàng)建一個ARKit項目筑凫,并且創(chuàng)建太陽决侈、地球這些球體,接下來我們來談一談如何讓它們動起來。
演示視頻:
天文科普
首先科普下太陽系的結(jié)構(gòu)叠必,太陽系共有八大行星肚逸,水星货徙、金星区拳、地球浅蚪、火星、木星烫罩、土星惜傲、天王星、海王星贝攒,還有顆矮行星冥王星盗誊。木星體積最大,且自轉(zhuǎn)周期最快饿这,它和土星浊伙、天王星都自帶行星環(huán)撞秋,地球衛(wèi)星是月球长捧,金星和水星是太陽系中唯二不帶衛(wèi)星的行星。太陽作為恒星本身會自轉(zhuǎn)吻贿,而行星除了自轉(zhuǎn)外還會圍繞它的恒心公轉(zhuǎn)串结,由于行星軌道多是橢圓,為了簡化難度(偷懶)我們假定他們的公轉(zhuǎn)軌道都是圓形舅列,而地球的自轉(zhuǎn)軌道也是斜的肌割,這些細(xì)節(jié)后面會進(jìn)一步完善。
3D模型創(chuàng)建--SceneKit
AR工程中有一個ARSCNView帐要,它用來加載3D模型的AR視圖的把敞,它繼承于SCNView,相對的加載2D視圖的就是ARSKView榨惠,視圖中的那些模型的創(chuàng)建運(yùn)動就需要用到本章所說的SceneKit和SpriteKit奋早。它們是iOS中用來開發(fā)3D模型和2D模型的引擎,由于沒用過Unity3D開發(fā)赠橙,所以此處不介紹耽装。
Sprite是用來創(chuàng)建2D模型,在游戲開發(fā)中期揪,指的是以圖像方式呈現(xiàn)在屏幕上的一個圖像掉奄。這個圖像也許可以移動,用戶可以與其交互凤薛,也有可能僅只是游戲的一個靜止的背景圖姓建。而在AR中,2D模型會隨著手機(jī)的遠(yuǎn)近放大縮小缤苫,而不能像3D模型那樣可以從側(cè)面觀察引瀑。
SceneKit 建立在 OpenGL 的基礎(chǔ)上,包含了如光照榨馁、模型憨栽、材質(zhì)、攝像機(jī)等高級引擎特性,我們可以基于它做出很多逼真的3D物理模型屑柔。
SCNScene & SCNNode
每個ARSCNView中都帶有一個場景SCNScene屡萤,它用來承載那些帶有幾何結(jié)構(gòu)、光度掸宛、相機(jī)以及其他屬性的節(jié)點(diǎn)SCNNode死陆,一個完整的3D場景就這么展現(xiàn)出來了。一個SCNScene可以包含多個SCNNode子節(jié)點(diǎn)唧瘾,它們一般都是呈樹狀結(jié)構(gòu)措译,一個子節(jié)點(diǎn)SCNNode可以有多個childNode,而SCNNode只有一個parentNode饰序,rootNode作為根節(jié)點(diǎn)领虹,我們通過rootNode添加自己的子節(jié)點(diǎn)SCNNode。
SCNNode的常用方法:
addChildNode(_:)
insertChildNode(_: atIndex:)
removeFromParentNode()
接下來介紹下SCNNode的幾種常用的屬性對象
** 1. SCNGeometry **
??SceneNode提供幾種幾何模型求豫,例如六面體(SCNBox)塌衰、平面(SCNPlane,只有一面)蝠嘉、無限平面(SCNFloor最疆,沿著x-z平面無限延伸)、球體(SCNSphere)等等蚤告。
例如我們創(chuàng)建一個半徑為0.25的球體
SCNNode *sunNode = [SCNNode new];
sunNode.geometry = [SCNSphere sphereWithRadius:0.25];
為了突出行星運(yùn)動軌跡努酸,我們給每顆星星添加了軌道,一開始我使用的是SCNPlane后來發(fā)現(xiàn)它只有一個平面杜恰,你從反面是看不到的获诈,于是我使用的是SCNBox
SCNNode *mercuryOrbit = [SCNNode node];
//設(shè)置不透明度
mercuryOrbit.opacity = 0.4;
//設(shè)置軌道的結(jié)構(gòu)體,height為0
mercuryOrbit.geometry = [SCNBox boxWithWidth:0.86 height:0 length:0.86 chamferRadius:0];
mercuryOrbit.geometry.firstMaterial.diffuse.contents = @"art.scnassets/solar/orbit.png";
//紋理濾波
mercuryOrbit.geometry.firstMaterial.diffuse.mipFilter = SCNFilterModeLinear;
mercuryOrbit.rotation = SCNVector4Make(0, 1, 0, M_PI_2);
//光照模式
mercuryOrbit.geometry.firstMaterial.lightingModelName = SCNLightingModelConstant; // no lighting
[_sunNode addChildNode:mercuryOrbit];
補(bǔ)充一下紋理濾波這個屬性有什么用?
當(dāng)材料表面的部分出現(xiàn)較大或小于原來的紋理圖像時箫章,紋理過濾決定了材料屬性的內(nèi)容的外觀
@property(nonatomic) SCNFilterMode minificationFilter
可選項
typedef enum : NSInteger {
SCNFilterModeNone = 0, // 當(dāng)這個位置沒有紋理顏色時烙荷,會采樣離他最近的顏色值
SCNFilterModeNearest = 1, //當(dāng)這個位置沒有紋理顏色時,線性插值顏色作為自己的顏色
SCNFilterModeLinear = 2, } SCNFilterMode;
默認(rèn)值為 SCNFilterModeLinear
** 2. SCNMaterial **
SceneNode提供8種屬性用來設(shè)置模型材質(zhì)
- Diffuse 漫發(fā)射屬性表示光和顏色在各個方向上的反射量
- Ambient 環(huán)境光以固定的強(qiáng)度和固定的顏色從表面上的所有點(diǎn)反射出來。如果場景中沒有環(huán)境光對象檬寂,這個屬性對節(jié)點(diǎn)沒有影響
- Specular 鏡面反射是直接反射到使用者身上的光線终抽,類似于鏡子反射光線的方式。此屬性默認(rèn)為黑色桶至,這將導(dǎo)致材料顯得呆滯
- Normal 正常照明是一種用于制造材料表面光反射的技術(shù)昼伴,基本上,它試圖找出材料的顛簸和凹痕镣屹,以提供更現(xiàn)實(shí)發(fā)光效果
- Reflective 反射光屬性是一個鏡像表面反射環(huán)境圃郊。表面不會真實(shí)地反映場景中的其他物體
- Emission 該屬性是由模型表面發(fā)出的顏色。默認(rèn)情況下女蜈,此屬性設(shè)置為黑色持舆。如果你提供了一個顏色色瘩,這個顏色就會體現(xiàn)出來,你可以提供一個圖像逸寓。SceneKit將使用此圖像提供“基于材料的發(fā)光效應(yīng)”居兆。
- Transparent 用來設(shè)置材質(zhì)的透明度
- Multiply 通過計算其他所有屬性的因素生成最終的合成的顏色
// 地球貼圖
_earthNode.geometry.firstMaterial.diffuse.contents = @"art.scnassets/solar/earth-diffuse-mini.jpg";
_earthNode.geometry.firstMaterial.emission.contents = @"art.scnassets/solar/earth-emissive-mini.jpg";
_earthNode.geometry.firstMaterial.specular.contents = @"art.scnassets/solar/earth-specular-mini.jpg";
另外我們對SCNNode進(jìn)行copy時,其屬性SCNMaterial并不會執(zhí)行深拷貝竹伸,也就是說被拷貝對象屬性只是對原來屬性的引用而已泥栖。
**3. SCNLight **
SceneNode中完全都是動態(tài)光照,提供四種類型的光照
- SCNLightTypeAmbient 環(huán)境光
- SCNLightTypeOmni 聚光燈
- SCNLightTypeDirectional 定向光源
- SCNLightTypeSpot 點(diǎn)光源
由于太陽作為太陽系的光源勋篓,所以我們需要能從各個角度看到它發(fā)光吧享,所以它的type = SCNLightTypeOmni,也就是聚光燈
//給sunNode添加光照
SCNNode *lightNode = [SCNNode node];
lightNode.light = [SCNLight light];
lightNode.light.color = [UIColor blackColor]; // initially switched off
lightNode.light.type = SCNLightTypeOmni;
[_sunNode addChildNode:lightNode];
// Configure attenuation distances because we don't want to light the floor
lightNode.light.attenuationEndDistance = 19;
lightNode.light.attenuationStartDistance = 21;
添加動畫--CoreAnimation
地球自轉(zhuǎn)動畫
//earthNode以y軸不停的旋轉(zhuǎn)譬嚣,每次旋轉(zhuǎn)的周期為1s钢颂。
[_earthNoderunAction:[SCNActionrepeatActionForever:[SCNActionrotateByX:0y:2z:0duration:1]]];
月球自轉(zhuǎn)動畫
CABasicAnimation*animation = [CABasicAnimationanimationWithKeyPath:@"rotation"];//月球自轉(zhuǎn)
animation.duration=1.5; //自轉(zhuǎn)周期1.5s
animation.toValue= [NSValuevalueWithSCNVector4:SCNVector4Make(0,1,0,M_PI*2)];//此處的意思是圍繞y軸([0,0,0]->[0,1,0])旋轉(zhuǎn)360°
animation.repeatCount=FLT_MAX;//重復(fù)次數(shù),此處無限次
[_moonNode addAnimation:animation forKey:@"moon rotation"];//將動畫添加至moonNode節(jié)點(diǎn)
接下來我們來實(shí)現(xiàn)月球隨著地球公轉(zhuǎn)
??moonRotationNode添加moonNode孤荣,moonNode由于與原點(diǎn)有偏移甸陌,moonRotation自轉(zhuǎn)后就實(shí)現(xiàn)了moonNode圍繞原點(diǎn)公轉(zhuǎn)了须揣,然后再加moonRotationNode添加至earthGroupNode即可盐股。
_moonNode.position=SCNVector3Make(0.1,0,0);//設(shè)置moon的位置
SCNNode*moonRotationNode = [SCNNodenode];
[moonRotationNodeaddChildNode:_moonNode];
// Rotate the moon around the Earth
CABasicAnimation*moonRotationAnimation = [CABasicAnimationanimationWithKeyPath:@"rotation"];
moonRotationAnimation.duration=15.0;
moonRotationAnimation.toValue= [NSValuevalueWithSCNVector4:SCNVector4Make(0,1,0,M_PI*2)];
moonRotationAnimation.repeatCount=FLT_MAX;
[moonRotationNodeaddAnimation:animationforKey:@"moon rotation around earth"];
[_earthGroupNodeaddChildNode:moonRotationNode];//將moonRotationNode添加至earthGroupNode節(jié)點(diǎn)
如何實(shí)現(xiàn)地球子系統(tǒng)圍繞太陽公轉(zhuǎn)
SCNNode*earthRotationNode = [SCNNodenode];
[_sunNodeaddChildNode:earthRotationNode];
// Earth-group (will contain the Earth, and the Moon)
[earthRotationNodeaddChildNode:_earthGroupNode];
// Rotate the Earth around the Sun
animation = [CABasicAnimationanimationWithKeyPath:@"rotation"];
animation.duration=30.0;
animation.toValue= [NSValuevalueWithSCNVector4:SCNVector4Make(0,1,0,M_PI*2)];
animation.repeatCount=FLT_MAX;
[earthRotationNodeaddAnimation:animationforKey:@"earth rotation around sun"];
同理其他幾顆星體也可以如此,由于土星自帶行星環(huán)耻卡,需要額外處理一下疯汁。
CABasicAnimation*animation = [CABasicAnimationanimationWithKeyPath:@"rotation"];//月球自轉(zhuǎn)
animation.duration=1.5; //自轉(zhuǎn)周期1.5s
animation.toValue= [NSValuevalueWithSCNVector4:SCNVector4Make(0,1,0,M_PI*2)];//此處的意思是圍繞y軸([0,0,0]->[0,1,0])旋轉(zhuǎn)360°
animation.repeatCount=FLT_MAX;//重復(fù)次數(shù),此處無限次
[_moonNode addAnimation:animation forKey:@"moon rotation"];//將動畫添加至moonNode節(jié)點(diǎn)
為了讓太陽的效果更佳逼真卵酪,我們給它增加了光環(huán)
// Add a halo to the Sun (a simple textured plane that does not write to depth)
_sunHaloNode = [SCNNode node];
_sunHaloNode.geometry = [SCNPlane planeWithWidth:2.5 height:2.5];
_sunHaloNode.rotation = SCNVector4Make(1, 0, 0, 0 * M_PI / 180.0);
_sunHaloNode.geometry.firstMaterial.diffuse.contents = @"art.scnassets/solar/sun-halo.png";
_sunHaloNode.geometry.firstMaterial.lightingModelName = SCNLightingModelConstant; // no lighting
_sunHaloNode.geometry.firstMaterial.writesToDepthBuffer = NO; // do not write to depth
_sunHaloNode.opacity = 0.2;
[_sunNode addChildNode:_sunHaloNode];
我們還給地球增加云層
SCNNode *cloudsNode = [SCNNode node];
cloudsNode.geometry = [SCNSphere sphereWithRadius:0.06];
[_earthNode addChildNode:cloudsNode];
cloudsNode.opacity = 0.5;
// This effect can also be achieved with an image with some transparency set as the contents of the 'diffuse' property
cloudsNode.geometry.firstMaterial.transparent.contents = @"art.scnassets/solar/cloudsTransparency.png";
cloudsNode.geometry.firstMaterial.transparencyMode = SCNTransparencyModeRGBZero;
以上我們就實(shí)現(xiàn)了太陽系的模型創(chuàng)建以及行星的自轉(zhuǎn)并周期的圍繞太陽公轉(zhuǎn)幌蚊,但是如何才能有更好的觀看效果呢,于是我們記起了上章講到的ARKit溃卡,通過ARSession的一個Delegate函數(shù)
//pragma mark -ARSessionDelegate
//會話位置更新
-- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame
{
//監(jiān)聽手機(jī)的移動溢豆,實(shí)現(xiàn)近距離查看太陽系細(xì)節(jié),為了凸顯效果變化值*3
[_sunNode setPosition:SCNVector3Make(
-3 * frame.camera.transform.columns[3].x,
-0.1 - 3 * frame.camera.transform.columns[3].y,
-2 - 3 * frame.camera.transform.columns[3].z)];
}
小結(jié)
這樣我們就完成了一個通過ARKit+SceneKit實(shí)現(xiàn)將太陽系裝進(jìn)iPhone的夢想了瘸羡,女朋友說我想要天上的星星漩仙,于是我打開了ARSolarPlay抓住了Solar,你看整個太陽系盡在我的掌中犹赖,說吧队他,你想要哪顆?簡直撩妹/漢神器有木有峻村。
代碼見同性交友網(wǎng)站:https://github.com/miliPolo/ARSolarPlay(OC實(shí)現(xiàn))
????????????????????https://github.com/miliPolo/ARSolarPlaySwift(Swift實(shí)現(xiàn))
上一篇文章:如何用ARKit將太陽系裝進(jìn)iPhone(一)
如果您覺得有價值麸折,請在github賞個star,不勝感激粘昨。
如果有什么想交流的垢啼,歡迎私信窜锯。