1. 程式人生 > >從零開始實現太陽公轉AR專案(swift)

從零開始實現太陽公轉AR專案(swift)

前言

我們一般建立ar專案都是Augumented Reality App,系統會給我們生成一些程式碼。今天我們我們就從普通的Single View App一步步建立實現ar專案

太陽公轉ar小專案

建立專案

這一部分是建立專案、然後建立從一個viewcontroller點選按鈕present進入到我們的SunRevolutionViewController。這些比較簡單,我就一筆帶過
這裡寫圖片描述

ps 由於用到相機,所以我們要新增相機許可權

 <key>NSCameraUsageDescription</key>
 <string>應用將要使用您的照相機</string
>

核心地帶

1. 初始化arview必須的類

  • 初始化arview必須的類
    ARSCNView(展示ar)
    ARSession (負責相機與模型的互動)
    ARWorldTrackingConfiguration(追蹤裝置方向的基本配置)
 let arSCNView = ARSCNView()
    let arSession = ARSession()
    let arConfiguration = ARWorldTrackingConfiguration()
  • 重寫viewviewapper,讓ARWorldTrackingConfiguration追蹤我們的配置
  override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)


        arConfiguration.isLightEstimationEnabled = true//自適應燈光(室內到室外的話 畫面會比較柔和)

        arSession.run(arConfiguration)
    }
  • 新增arview,並設定代理
class SunRevolutionViewController: UIViewController,ARSCNViewDelegate
{
    //設定arSCNView屬性
        arSCNView.frame = self.view.frame

        arSCNView.session = arSession
        arSCNView.automaticallyUpdatesLighting = true//自動調節亮度



        self.view.addSubview(arSCNView)

        arSCNView.delegate = self

此時的效果圖

2 新增太陽、地球、月亮節點

新增太陽、地球、月亮節點,讓他們顯示在我們的螢幕上

我們讓月亮節點放到地球節點上,地球節點放到太陽節點上

//初始化節點資訊
    func initNode()  {

        //1.設定幾何
        sunNode.geometry = SCNSphere(radius: 3)
        earthNode.geometry =  SCNSphere(radius: 1)
        moonNode.geometry =  SCNSphere(radius: 0.5)
        //2.渲染圖
     // multiply: 把整張圖拉伸,之後會變淡
     //diffuse:平均擴散到整個物體的表面,平切光澤透亮
   //   AMBIENT、DIFFUSE、SPECULAR屬性。這三個屬性與光源的三個對應屬性類似,每一屬性都由四個值組成。AMBIENT表示各種光線照射到該材質上,經過很多次反射後最終遺留在環境中的光線強度(顏色)。DIFFUSE表示光線照射到該材質上,經過漫反射後形成的光線強度(顏色)。SPECULAR表示光線照射到該材質上,經過鏡面反射後形成的光線強度(顏色)。通常,AMBIENT和DIFFUSE都取相同的值,可以達到比較真實的效果。
//        EMISSION屬性。該屬性由四個值組成,表示一種顏色。OpenGL認為該材質本身就微微的向外發射光線,以至於眼睛感覺到它有這樣的顏色,但這光線又比較微弱,以至於不會影響到其它物體的顏色。
//        SHININESS屬性。該屬性只有一個值,稱為“鏡面指數”,取值範圍是0128。該值越小,表示材質越粗糙,點光源發射的光線照射到上面,也可以產生較大的亮點。該值越大,表示材質越類似於鏡面,光源照射到上面後,產生較小的亮點。

        sunNode.geometry?.firstMaterial?.multiply.contents = "art.scnassets/earth/sun.jpg"
        sunNode.geometry?.firstMaterial?.diffuse.contents = "art.scnassets/earth/sun.jpg"
        sunNode.geometry?.firstMaterial?.multiply.intensity = 0.5 //強度
        sunNode.geometry?.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant
        //  地球圖

        earthNode.geometry?.firstMaterial?.diffuse.contents = "art.scnassets/earth/earth-diffuse-mini.jpg"
        //  地球夜光圖
         earthNode.geometry?.firstMaterial?.emission.contents = "art.scnassets/earth/earth-emissive-mini.jpg";
         earthNode.geometry?.firstMaterial?.specular.contents = "art.scnassets/earth/earth-specular-mini.jpg";

        //    月球圖
        moonNode.geometry?.firstMaterial?.diffuse.contents = "art.scnassets/earth/moon.jpg";

        //3.設定位置

        sunNode.position = SCNVector3(0, 5, -20)

        earthNode.position = SCNVector3(10, 0, 0)

        moonNode.position = SCNVector3(3, 0, 0)

        //4.讓rootnode為sun sun上新增earth earth新增moon

        sunNode.addChildNode(earthNode)

        earthNode.addChildNode(moonNode)


        self.arSCNView.scene.rootNode.addChildNode(sunNode)
    }

此時我們的三個節點顯示出來了
這裡寫圖片描述

3 設定轉動

設定太陽自轉

 //MARK:設定太陽自轉
    func sunRotation()  {
        let animation = CABasicAnimation(keyPath: "rotation")

        animation.duration = 10.0//速度

        animation.toValue = NSValue(scnVector4: SCNVector4(0, 1, 0, Double.pi * 2))//圍繞自己的y軸轉動

        animation.repeatCount = Float.greatestFiniteMagnitude

        sunNode.addAnimation(animation, forKey: "sun-texture")



    }

太陽自轉

由於地球和月球都放到了太陽節點上,所以地球和月球會跟著太陽轉動

設定地球地球和月球之間的轉動

  earthNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)), forKey: "earth-texture")//duration標識速度 數字越小數字速度越快
        //設定月球自轉

地球自轉

duration標識速度 數字越小數字速度越快 比如修改數字為0.1後的效果

電動小月球

由於月球公轉和地球自轉的週期不一致,所以月球不能放到地球節點上
建立一個月球圍繞地球節點(與地球節點位置相同),讓月球放到地月節點上,讓這個節點自轉,設定轉動速度即可

程式碼修改為

 let moonRotationNode = SCNNode()//月球圍繞地球轉動的節點
  earthNode.position = SCNVector3(3, 0, 0)

 moonRotationNode.position = earthNode.position //設定月球圍繞地球轉動的節點位置與地球的位置相同
func earthTurn()  {
       //蘋果有一套自帶的動畫
        earthNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)), forKey: "earth-texture")//duration標識速度 數字越小數字速度越快
        //設定月球自轉
        let animation = CABasicAnimation(keyPath: "rotation")

        animation.duration = 1.5//速度

        animation.toValue = NSValue(scnVector4: SCNVector4(0, 1, 0, Double.pi * 2))//圍繞自己的y軸轉動

        animation.repeatCount = Float.greatestFiniteMagnitude

        moonNode.addAnimation(animation, forKey: "moon-rotation")//月球自轉

        //設定月球公轉
        let moonRotationAnimation = CABasicAnimation(keyPath: "rotation")

        moonRotationAnimation.duration = 5//速度

        moonRotationAnimation.toValue = NSValue(scnVector4: SCNVector4(0, 1, 0, Double.pi * 2))//圍繞自己的y軸轉動

        moonRotationAnimation.repeatCount = Float.greatestFiniteMagnitude


        moonRotationNode.addAnimation(moonRotationAnimation, forKey: "moon rotation around earth")


    }

設定公轉

公轉和月球圍繞地球轉動類似,建立一個地月節點,地月節點上防止地球節點和月球圍繞地球節點,月球圍繞地球節點放置月球節點,如圖所示
節點關係圖

程式碼

 let earthGroupNode =  SCNNode()//地球和月球當做一個整體的節點 圍繞太陽公轉需要
   //3.設定位置

        sunNode.position = SCNVector3(0, 5, -20)


        earthGroupNode.position = SCNVector3(10,0,0)//地月節點距離太陽的10

        earthNode.position = SCNVector3(3, 0, 0)

        moonRotationNode.position = earthNode.position //設定月球圍繞地球轉動的節點位置與地球的位置相同


        moonNode.position = SCNVector3(3, 0, 0)//月球距離月球圍繞地球轉動距離3

        //4.讓rootnode為sun sun上新增earth earth新增moon

//        sunNode.addChildNode(earthNode)

//        earthNode.addChildNode(moonNode)

        moonRotationNode.addChildNode(moonNode)

        earthGroupNode.addChildNode(earthNode)
        earthGroupNode.addChildNode(moonRotationNode)


        sunNode.addChildNode(earthGroupNode)


        self.arSCNView.scene.rootNode.addChildNode(sunNode)

最終效果圖
公轉

新增光的效果

 //MARK://設定太陽光暈和被光找到的地方
    func addLight() {

        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light?.color = UIColor.red //被光找到的地方顏色


        sunNode.addChildNode(lightNode)

        lightNode.light?.attenuationEndDistance = 20.0 //光照的亮度隨著距離改變
        lightNode.light?.attenuationStartDistance = 1.0

        SCNTransaction.begin()


        SCNTransaction.animationDuration = 1



        lightNode.light?.color =  UIColor.white
        lightNode.opacity = 0.5 // make the halo stronger

        SCNTransaction.commit()

        sunHaloNode.geometry = SCNPlane.init(width: 25, height: 25)

        sunHaloNode.rotation = SCNVector4Make(1, 0, 0, Float(0 * Double.pi / 180.0))
        sunHaloNode.geometry?.firstMaterial?.diffuse.contents = "art.scnassets/earth/sun-halo.png"
        sunHaloNode.geometry?.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant // no lighting
        sunHaloNode.geometry?.firstMaterial?.writesToDepthBuffer = false // 不要有厚度,看起來薄薄的一層
        sunHaloNode.opacity = 5

        sunHaloNode.addChildNode(sunHaloNode)
    }

最終效果

可以看到地球被光找到的地方會發亮,還有太陽周圍有一層光暈

程式碼地址: