2017-06-15 16 views
0

私は最初のSceneKitアプリケーションを試しています。私の目標は、地球の表面からのビューをシミュレートし、デバイスのカメラを任意の方向に向け、カメラビュー上に情報を重ねることができるようにすることです。SceneKitビューが後方にレンダリングされる

まず、SceneKitカメラビューをデバイスの向きに合わせようとしています。必要に応じて動作していることを確認するために、特定の緯度と経度の座標で球を追加しています。

1つの重要な問題以外はすべて動作しています。ビューは、表示されるべきものから左右(東/西)にミラーリングされます。私は時間をカメラの調整の異なる順列をしようと過ごした。

以下は私の完全なテストアプリビューコントローラです。シーンを適切にレンダリングするための変更の適切な組み合わせを理解することはできません。北極の球は正しいです。私の現在の経度で、北極から赤道まで伸びる球の線は、予想通りオーバーヘッドに見えます。それは正しくない他の球体です。鏡は自分の経度にあるかのように、彼らは東西に映し出されます。

このコードをテストする場合は、SceneKitで新しいGameプロジェクトを作成します。テンプレートGameViewController.swiftファイルを以下のファイルに置き換えます。また、Info.plistに「Privacy - Location When In Use Description」キーを追加する必要があります。 for lon in ...行を調整して、数字が自分の経度で始まるか終わるようにすることもお勧めします。次に、球がディスプレイの正しい部分に描かれているかどうかを見ることができます。これはまた、UIColor(hue: CGFloat(lon + 104) * 2/255.0の色を少し調整する必要があります。

import UIKit 
import QuartzCore 
import SceneKit 
import CoreLocation 
import CoreMotion 

let EARTH_RADIUS = 6378137.0 

class GameViewController: UIViewController, CLLocationManagerDelegate { 
    var motionManager: CMMotionManager! 
    var scnCameraArm: SCNNode! 
    var scnCamera: SCNNode! 
    var locationManager: CLLocationManager! 
    var pitchAdjust = 1.0 
    var rollAdjust = -1.0 
    var yawAdjust = 0.0 

    func radians(_ degrees: Double) -> Double { 
     return degrees * Double.pi/180 
    } 

    func degrees(_ radians: Double) -> Double { 
     return radians * 180/Double.pi 
    } 

    func setCameraPosition(lat: Double, lon: Double, alt: Double) { 
     let yaw = lon 
     let pitch = lat 

     scnCameraArm.eulerAngles.y = Float(radians(yaw)) 
     scnCameraArm.eulerAngles.x = Float(radians(pitch)) 
     scnCameraArm.eulerAngles.z = 0 
     scnCamera.position = SCNVector3(x: 0.0, y: 0.0, z: Float(alt + EARTH_RADIUS)) 
    } 

    func setCameraPosition(loc: CLLocation) { 
     setCameraPosition(lat: loc.coordinate.latitude, lon: loc.coordinate.longitude, alt: loc.altitude) 
    } 

    // MARK: - UIViewController methods 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     // create a new scene 
     let scene = SCNScene() 

     let scnCamera = SCNNode() 
     let camera = SCNCamera() 
     camera.zFar = 2.5 * EARTH_RADIUS 
     scnCamera.camera = camera 
     scnCamera.position = SCNVector3(x: 0.0, y: 0.0, z: Float(EARTH_RADIUS)) 
     self.scnCamera = scnCamera 

     let scnCameraArm = SCNNode() 
     scnCameraArm.position = SCNVector3(x: 0, y: 0, z: 0) 
     scnCameraArm.addChildNode(scnCamera) 
     self.scnCameraArm = scnCameraArm 

     scene.rootNode.addChildNode(scnCameraArm) 

     // create and add an ambient light to the scene 
     let ambientLightNode = SCNNode() 
     ambientLightNode.light = SCNLight() 
     ambientLightNode.light!.type = .ambient 
     ambientLightNode.light!.color = UIColor.darkGray 
     scene.rootNode.addChildNode(ambientLightNode) 

     // retrieve the SCNView 
     let scnView = self.view as! SCNView 

     // set the scene to the view 
     scnView.scene = scene 
     //scnView.pointOfView = scnCamera 

     // Draw spheres over part of the western hemisphere 
     for lon in stride(from: 0, through: -105, by: -15) { 
      for lat in stride(from: 0, through: 90, by: 15) { 
       let mat4 = SCNMaterial() 
       if lat == 90 { 
        mat4.diffuse.contents = UIColor.yellow 
       } else if lat == -90 { 
        mat4.diffuse.contents = UIColor.orange 
       } else { 
        //mat4.diffuse.contents = UIColor(red: CGFloat(lat + 90)/255.0, green: CGFloat(lon + 104) * 4/255.0, blue: 1, alpha: 1) 
        mat4.diffuse.contents = UIColor(hue: CGFloat(lon + 104) * 2/255.0, saturation: 1, brightness: CGFloat(255 - lat * 2)/255.0, alpha: 1) 
       } 

       let ball = SCNSphere(radius: 100000) 
       ball.firstMaterial = mat4 
       let ballNode = SCNNode(geometry: ball) 
       ballNode.position = SCNVector3(x: 0.0, y: 0.0, z: Float(100000 + EARTH_RADIUS)) 

       let ballArm = SCNNode() 
       ballArm.position = SCNVector3(x: 0, y: 0, z: 0) 
       ballArm.addChildNode(ballNode) 
       scene.rootNode.addChildNode(ballArm) 

       ballArm.eulerAngles.y = Float(radians(Double(lon))) 
       ballArm.eulerAngles.x = Float(radians(Double(lat))) 
      } 
     } 

     // configure the view 
     scnView.backgroundColor = UIColor(red: 0, green: 191/255, blue: 255/255, alpha: 1) // sky blue 

     locationManager = CLLocationManager() 
     locationManager.delegate = self 
     locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation 
     let auth = CLLocationManager.authorizationStatus() 
     switch auth { 
     case .authorizedWhenInUse: 
      locationManager.startUpdatingLocation() 
     case .notDetermined: 
      locationManager.requestWhenInUseAuthorization() 
     default: 
      break 
     } 

     motionManager = CMMotionManager() 
     motionManager.deviceMotionUpdateInterval = 1/30 
     motionManager.startDeviceMotionUpdates(using: .xTrueNorthZVertical, to: OperationQueue.main) { (motion, error) in 
      if error == nil { 
       if let motion = motion { 
        //print("pitch: \(self.degrees(motion.attitude.roll * self.pitchAdjust)), roll: \(self.degrees(motion.attitude.pitch * self.rollAdjust)), yaw: \(self.degrees(-motion.attitude.yaw))") 
        self.scnCamera.eulerAngles.z = Float(motion.attitude.yaw + self.yawAdjust) 
        self.scnCamera.eulerAngles.x = Float(motion.attitude.roll * self.pitchAdjust) 
        self.scnCamera.eulerAngles.y = Float(motion.attitude.pitch * self.rollAdjust) 
       } 
      } 
     } 
    } 

    override func viewWillAppear(_ animated: Bool) { 
     super.viewWillAppear(animated) 

     if UIApplication.shared.statusBarOrientation == .landscapeRight { 
      pitchAdjust = -1.0 
      rollAdjust = 1.0 
      yawAdjust = Double.pi 
     } else { 
      pitchAdjust = 1.0 
      rollAdjust = -1.0 
      yawAdjust = 0.0 
     } 
    } 

    override var shouldAutorotate: Bool { 
     return false 
    } 

    override var prefersStatusBarHidden: Bool { 
     return true 
    } 

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask { 
     return .landscape 
    } 

    // MARK: - CLLocationManagerDelegate methods 

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { 
     if status == .authorizedWhenInUse { 
      manager.startUpdatingLocation() 
     } 
    } 

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { 
     for loc in locations { 
      print(loc) 
      if loc.horizontalAccuracy > 0 && loc.horizontalAccuracy <= 100 { 
       setCameraPosition(loc: loc) 
      } 
     } 
    } 

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { 

    } 
} 

変化は、おそらくsetCameraPosition方法および/またはviewDidLoadの終わりにmotionManager.startDeviceMotionUpdates閉鎖のいくつかの組み合わせで行う必要があります。

答えて

1

感謝。シーンエディタでそのシーンを見た後、ヨウオイラー角(Y軸)の回転が私が思っていたものと逆だったので、すべてが左右反転されていることに気付きました。したがってノードのeulerAngles.yを希望の経度に設定すると、私は自分が望む方向とは逆の方向に回転していました。しかし、カメラに同じエラーがあったので、私の場所の球は正しい場所にありましたが、他のものはすべて左から右に反転していました。

解決策は、球とカメラの位置の両方の経度値を無効にすることでした。

scnCameraArm.eulerAngles.y = Float(radians(yaw)) 

ニーズもします

scnCameraArm.eulerAngles.y = -Float(radians(yaw)) 

そして:

ballArm.eulerAngles.y = Float(radians(Double(lon))) 

をする必要があります:

ballArm.eulerAngles.y = -Float(radians(Double(lon))) 
1

私は何が間違っているのか正確には分かりませんが、eulerAnglesでSceneKitがピッチとヨーを使用する方法は、あなたが描いている方法と一致しません。正のピッチ、ロール、ヨーがどちらの方向であるかを指定する章/節を見つけることができませんでした。

私は、

私はまた、いくつかのデバッグの色やラベルを追加
ballArm.eulerAngles.x = -1 * Float(radians(Double(lat))) 

にピッチ/ヨーの設定を変更することにより、(少なくとも私が思うに、私はあなたがしようとしていた何をやった)、それは働いて得ることができましたシーンを.SCN形式にアーカイブしてXcode Scene Editorにロードします(Xcode 9はデバッガでこれを行います)。各ノードに名前を付けると、エディターに何が表示されているかを確認できます。改訂コード:

// Draw spheres over part of the western hemisphere 
    for lon in stride(from: 0, through: -105, by: -15) { 
     for lat in stride(from: 0, through: 90, by: 15) { 
      let mat4 = SCNMaterial() 
      if lon == 0 { 
       mat4.diffuse.contents = UIColor.black 
      } else if lon == -105 { 
       mat4.diffuse.contents = UIColor.green 
      } else if lat == 90 { 
       mat4.diffuse.contents = UIColor.yellow 
      } else if lat == 0 { 
       mat4.diffuse.contents = UIColor.orange 
      } else { 
       mat4.diffuse.contents = UIColor(red: CGFloat(lat + 90)/255.0, green: CGFloat(lon + 104) * 4/255.0, blue: 1, alpha: 1) 
       //mat4.diffuse.contents = UIColor(hue: CGFloat(lon + 104) * 2/255.0, saturation: 1, brightness: CGFloat(255 - lat * 2)/255.0, alpha: 1) 
       //mat4.diffuse.contents = UIColor.green 
      } 

      let ball = SCNSphere(radius: 100000) 
      ball.firstMaterial = mat4 
      let ballNode = SCNNode(geometry: ball) 
      ballNode.position = SCNVector3(x: 0.0, y: 0.0, z: Float(100000 + EARTH_RADIUS)) 

      let ballArm = SCNNode() 
      ballArm.position = SCNVector3(x: 0, y: 0, z: 0) 
// debugging label 
      ballArm.name = "\(lat) \(lon)" 
      ballArm.addChildNode(ballNode) 
      scene.rootNode.addChildNode(ballArm) 

      ballArm.eulerAngles.y = Float(radians(Double(lon))) 
      ballArm.eulerAngles.x = -1 * Float(radians(Double(lat))) 
     } 
    } 
    // configure the view 
    scnView.backgroundColor = UIColor.cyan 

    let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) 
    let docsFolderURL = urls[urls.count - 1] 
    let archiveURL = docsFolderURL.appendingPathComponent("rmaddy.scn") 
    let archivePath = archiveURL.path 
// for copy/paste from Simulator's file system 
    print (archivePath) 
    let archiveResult = NSKeyedArchiver.archiveRootObject(scene, toFile: archivePath) 
    print(archiveResult) 

ここでは、シーンエディタのシーンのスクリーンショットを示します。ノードリストで選択されたlat 45/lon -90ノードの球の周りの薄いオレンジ色の円に注意してください。私のシーンから「SCN」ファイルを作成するための彼の非常に役立つ提案をハルに

enter image description here

+0

は正しい方向に私を指してくれてありがとう。 "scn"ファイルを作成し、シーンエディタで見ているあなたの提案は、私の世界が映っていることを私に示しました。私は間違った方向にy軸を中心に回転していました。したがって、実際には、 'x '値ではなくネゲートする必要がある' eulerAngles.y'です。そして私はカメラと球の両方でそうする必要がありました。だからあなたの提案された答えは実際に私の問題の正しい解決策ではありませんが、あなたの答えは私に正しい解決策を見いだすのに非常に役立ちました。 – rmaddy

関連する問題