ARkit連載二之太陽系

在上一節(jié)中簡單介紹ARKit及其所依賴的SceneKit,但是還有好多API中的東西沒有介紹元莫,詳情可查看http://blog.csdn.net/u013263917/article/details/73156679赖阻,最好還能自己研究一下,這里東西不多且難度不大柒竞,你一定會有更多收獲政供。另外這方面開發(fā)主要在于創(chuàng)建3D場景,也就是使用SceneKit朽基。
下面是一個從零開始的太陽系demo布隔,主要內(nèi)容是節(jié)點(diǎn)與動畫。希望能幫助大家理解在3D場景中稼虎,萬物皆節(jié)點(diǎn)衅檀。
呵呵,給了一句自己都似懂非懂的話霎俩,其實(shí)我自己想過為什么動畫不是節(jié)點(diǎn)呢哀军?在3D世界,還有時(shí)間的概念等等打却,他們是或者可以是節(jié)點(diǎn)嗎杉适?不過貌似在SceneKit中,動畫是作為節(jié)點(diǎn)的屬性或者方法存在柳击,而不是子節(jié)點(diǎn)猿推。
不想太多,我們還是挺近太陽系吧捌肴!

太陽系

1, 創(chuàng)建一個空項(xiàng)目蹬叭,搭建一個最初AR代碼環(huán)境

  • Info.plist添加照相機(jī)權(quán)限
    <key>NSCameraUsageDescription</key>
    <string>This application will use the camera for Augmented Reality.</string>
  • 導(dǎo)入ARKit、SceneKit
import ARKit
import SceneKit
  • 添加ARSCNView状知,并設(shè)置代理與ARSession秽五、ARWorldTrackingConfiguration
    lazy var arSCNView: ARSCNView = {
        let arSCNView = ARSCNView(frame: view.bounds)
        arSCNView.session = arSession
        arSCNView.automaticallyUpdatesLighting = true
        return arSCNView
    }()
    lazy var arSession: ARSession = ARSession()
    var arConfiguration: ARConfiguration!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(arSCNView)
        arSCNView.delegate = self

       // 設(shè)置節(jié)點(diǎn)與動畫
    }
   
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        arConfiguration = ARWorldTrackingConfiguration()
        arConfiguration.isLightEstimationEnabled = true
        arSession.run(arConfiguration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        arSession.pause()
    }

2, 創(chuàng)建節(jié)點(diǎn),并使用節(jié)點(diǎn)之間的層級結(jié)構(gòu)處理旋轉(zhuǎn)饥悴。節(jié)點(diǎn)有點(diǎn)像layer坦喘,都可以添加動畫盲再,只不過在展示上,節(jié)點(diǎn)是3D的瓣铣。如果earthNode作為sunNode的子節(jié)點(diǎn)洲胖,然后讓sunNode自身旋轉(zhuǎn),則earthNode會以sunNode為原點(diǎn)坯沪,繞半徑旋轉(zhuǎn)。月球繞地球旋轉(zhuǎn)同理擒滑。

    func setupNodes() {
        sunNode = SCNNode()
        earthNode = SCNNode()
        moonNode = SCNNode()
        
        sunNode.geometry = SCNSphere(radius: 3)
        earthNode.geometry = SCNSphere(radius: 1)
        moonNode.geometry = SCNSphere(radius: 0.3)
        
        sunNode.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "sun.jpg")
        earthNode.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "earth-diffuse-mini.jpg")
        moonNode.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "moon.jpg")
        
        sunNode.position = SCNVector3Make(0, 0, -30)
        earthNode.position = SCNVector3Make(10, 0, 0)
        moonNode.position = SCNVector3Make(2, 0, 0)
        
        arSCNView.scene.rootNode.addChildNode(sunNode!)
        sunNode?.addChildNode(earthNode!)
        earthNode?.addChildNode(moonNode!)
    }
    
    func setupAnimation() {
        let animation = CABasicAnimation(keyPath: "rotation")
        animation.duration = 3
        animation.toValue = SCNVector4(0, 1, 0, Float.pi * 2)
        animation.repeatCount = MAXFLOAT
        earthNode.addAnimation(animation, forKey: "moon rotation around earth")
        
        let animation2 = CABasicAnimation(keyPath: "rotation")
        animation2.duration = 10
        animation2.toValue = SCNVector4(0, 1, 0, Float.pi * 2)
        animation2.repeatCount = MAXFLOAT
        sunNode.addAnimation(animation, forKey: "earth rotation around sun")
    }
太陽系1.gif

3, 從以上效果看腐晾,貌似已經(jīng)基本實(shí)現(xiàn)了。但總有點(diǎn)怪丐一,因?yàn)榈厍蚬D(zhuǎn)不是跟太陽自傳同步的藻糖,月亮公轉(zhuǎn)也不應(yīng)該跟地球自傳同步。所以上面那種簡單的做法是不行库车,我們必須將公轉(zhuǎn)與自轉(zhuǎn)剝離開巨柒。

  • 我們創(chuàng)建一個與太陽同層級的節(jié)點(diǎn),并且與太陽的位置相同柠衍。該節(jié)點(diǎn)有點(diǎn)像黃道洋满,所以就取名黃道吧。
  • 讓地球作為黃道的子節(jié)點(diǎn)珍坊。也就是讓黃道控制了地球的公轉(zhuǎn)牺勾。
  • 月亮公轉(zhuǎn)同上。
  • 在這里不管是公轉(zhuǎn)還是自轉(zhuǎn)阵漏,其實(shí)都是自轉(zhuǎn)驻民。
    var earthPathNode: SCNNode! // 黃道(ecliptic),控制地球公轉(zhuǎn)
    var moonPathNode: SCNNode! // 白道履怯,控制月球公轉(zhuǎn)

    // 這兩個節(jié)點(diǎn)回还,如果只為地球公轉(zhuǎn)與月球公轉(zhuǎn),則不需要幾何形與渲染
    earthPathNode = SCNNode()
    moonPathNode = SCNNode()
    // 位置
    sunNode.position = SCNVector3Make(0, -10, -20)
    earthPathNode.position = sunNode.position
        
    earthNode.position = SCNVector3Make(10, 0, 0)
    moonPathNode.position = earthNode.position
        
    moonNode.position = SCNVector3Make(3, 0, 0)

    // 節(jié)點(diǎn)層級關(guān)系
    arSCNView.scene.rootNode.addChildNode(sunNode)
    arSCNView.scene.rootNode.addChildNode(earthPathNode)
        
    earthPathNode.addChildNode(earthNode)
    earthPathNode.addChildNode(moonPathNode)

    moonPathNode.addChildNode(moonNode)

    func setupAnimation() {
        // 月亮自轉(zhuǎn)
        moonNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 4, z: 0, duration: 1)))
        
        // 地球自轉(zhuǎn)
        earthNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
        
        // 太陽自轉(zhuǎn)叹洲,這里采用
        var sunAnimation = CABasicAnimation(keyPath: "contentsTransform")
        sunAnimation.duration = 10.0
        sunAnimation.fromValue = CATransform3DConcat(CATransform3DMakeTranslation(0, 0, 0), CATransform3DMakeScale(3, 3, 3))
        sunAnimation.fromValue = CATransform3DConcat(CATransform3DMakeTranslation(1, 0, 0), CATransform3DMakeScale(3, 3, 3))
        sunAnimation.repeatCount = MAXFLOAT
        sunNode.geometry?.firstMaterial?.diffuse.addAnimation(sunAnimation, forKey: "sun rotation")
        
        sunAnimation = CABasicAnimation(keyPath: "contentsTransform")
        sunAnimation.duration = 30.0
        sunAnimation.fromValue = CATransform3DConcat(CATransform3DMakeTranslation(0, 0, 0), CATransform3DMakeScale(5, 5, 5))
        sunAnimation.fromValue = CATransform3DConcat(CATransform3DMakeTranslation(1, 0, 0), CATransform3DMakeScale(5, 5, 5))
        sunAnimation.repeatCount = MAXFLOAT
        sunNode.geometry?.firstMaterial?.multiply.addAnimation(sunAnimation, forKey: "sun rotation2")
        
        // 月亮公轉(zhuǎn)
        moonPathNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 3, z: 0, duration: 1)))
        
        // 地球公轉(zhuǎn)
        earthPathNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 1, z: 0, duration: 1)))

        /*
        // 月亮公轉(zhuǎn)
        let moonPathAnimation = CABasicAnimation(keyPath: "rotation")
        moonPathAnimation.duration = 3
        moonPathAnimation.toValue = SCNVector4(0, 1, 0, Float.pi * 2)
        moonPathAnimation.repeatCount = MAXFLOAT
        moonPathNode.addAnimation(moonPathAnimation, forKey: "moon rotation around earth")
        */
    }
太陽系2.gif

4, 添加太陽光柠硕、暈、地球公轉(zhuǎn)軌道

    var sunHaloNode: SCNNode? // 太陽光環(huán)(暈)
    func setupLight() {
        // 太陽光
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light?.color = UIColor.black
        lightNode.light?.type = .omni
        lightNode.light?.attenuationStartDistance = 3.0
        lightNode.light?.attenuationEndDistance = 20.0
        sunNode.addChildNode(lightNode)
        
        // 動畫
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 1
        SCNTransaction.completionBlock = {
            lightNode.light?.color = UIColor.white
            self.sunHaloNode?.opacity = 0.5
        }
        SCNTransaction.commit()

        // 暈
        sunHaloNode = SCNNode()
        sunHaloNode?.geometry = SCNPlane(width: 25, height: 25)
        sunHaloNode?.rotation = SCNVector4Make(1, 0, 0, 0)
        sunHaloNode?.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "sun-halo")
        sunHaloNode?.geometry?.firstMaterial?.lightingModel = .constant
        sunHaloNode?.geometry?.firstMaterial?.writesToDepthBuffer = false
        sunHaloNode?.opacity = 0.9
        sunNode.addChildNode(sunHaloNode!)
        
        // 地球公轉(zhuǎn)軌道
        let earthOrbitNode = SCNNode()
        earthOrbitNode.opacity = 0.4
        earthOrbitNode.geometry = SCNPlane(width: 21, height: 21)
        earthOrbitNode.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "orbit")
        earthOrbitNode.geometry?.firstMaterial?.multiply.contents = UIImage(named: "orbit")
        earthOrbitNode.geometry?.firstMaterial?.lightingModel = .constant
        earthOrbitNode.geometry?.firstMaterial?.diffuse.mipFilter = .linear
        earthOrbitNode.rotation = SCNVector4Make(1, 0, 0, -Float.pi/2)
        earthOrbitNode.geometry?.firstMaterial?.lightingModel = .constant
        sunNode.addChildNode(earthOrbitNode)
    }
太陽系3.gif

這里圖片背景是黑的疹味,是因?yàn)槲覔踝×藬z像頭仅叫,另外太陽暈的效果與真實(shí)效果也稍有不一樣。

好了糙捺,太陽系就這么愉快的完成了诫咱,是不是特別酷炫,特別拽呢洪灯?
“萬物皆節(jié)點(diǎn)”更多理解:雖然動畫不是節(jié)點(diǎn)坎缭,但就這個項(xiàng)目而言竟痰。為了動畫,我們專門創(chuàng)建了一個空白節(jié)點(diǎn)掏呼,然后讓需要改動畫效果的節(jié)點(diǎn)成為該節(jié)點(diǎn)的子節(jié)點(diǎn)坏快。從這個方面講,我[們]是不是可以將其理解成動畫也是節(jié)點(diǎn)呢憎夷?莽鸿!
ARKit不止如此,未完待續(xù)...
太陽系完整代碼:https://github.com/taoGod/ARKit2.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拾给,一起剝皮案震驚了整個濱河市祥得,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒋得,老刑警劉巖级及,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異额衙,居然都是意外死亡饮焦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門窍侧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來县踢,“玉大人,你說我怎么就攤上這事疏之〉钛” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵锋爪,是天一觀的道長丙曙。 經(jīng)常有香客問我,道長其骄,這世上最難降的妖魔是什么亏镰? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮拯爽,結(jié)果婚禮上索抓,老公的妹妹穿的比我還像新娘。我一直安慰自己毯炮,他們只是感情好逼肯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桃煎,像睡著了一般篮幢。 火紅的嫁衣襯著肌膚如雪为迈。 梳的紋絲不亂的頭發(fā)上搜锰,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天狈涮,我揣著相機(jī)與錄音,去河邊找鬼骆姐。 笑死玻褪,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灿里,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼色鸳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凡怎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后岳链,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牵辣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年择浊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逾条。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡近她,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出膳帕,到底是詐尸還是另有隱情粘捎,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布危彩,位于F島的核電站攒磨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏汤徽。R本人自食惡果不足惜娩缰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谒府。 院中可真熱鬧拼坎,春花似錦浮毯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盛龄,卻和暖如春饰迹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背余舶。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工啊鸭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人匿值。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓赠制,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挟憔。 傳聞我的和親對象是個殘疾皇子憎妙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載請注明原作者 上篇文章我們介紹如何創(chuàng)建一個ARKit項(xiàng)目,并且創(chuàng)建太陽曲楚、地球這些球體,接下來我們來談一談如何讓...
    miliPolo閱讀 7,987評論 15 41
  • 簡介: 上篇回顧: ARKit初探篇(鏈接)中寫到怎樣開啟一個AR項(xiàng)目,包括開發(fā)環(huán)境,建立項(xiàng)目,及基礎(chǔ)代碼實(shí)現(xiàn),在...
    a437e8f87a81閱讀 3,536評論 5 38
  • ARKit ARKit框架通過集成iOS設(shè)備攝像頭和運(yùn)動功能褥符,在您的應(yīng)用程序或游戲中產(chǎn)生增強(qiáng)現(xiàn)實(shí)體驗(yàn)龙誊。 概述 增強(qiáng)...
    暗夜夜夜行路閱讀 5,788評論 0 17
  • 對于每一個藝術(shù)生,心中都有一段關(guān)于藝考的美好回憶喷楣,現(xiàn)在來分享我的經(jīng)歷吧趟大! 懷揣著夢想,七月份我來到一家培訓(xùn)機(jī)構(gòu)铣焊,在...
    獅子座的逸軒閱讀 271評論 1 1
  • 上車逊朽,睡覺,做夢 北京朋友說 復(fù)興號上的夢一定會實(shí)現(xiàn)
    格桑朵拉閱讀 225評論 1 1