SceneKit视图向后呈现 [英] SceneKit view is rendered backwards

查看:96
本文介绍了SceneKit视图向后呈现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试我的第一个SceneKit应用。我的目标是模拟地球表面的视图,并能够将设备的相机指向任何方向,并在相机视图上覆盖信息。



首先,我只是试图获取SceneKit相机视图以匹配设备的方向。为了验证它是否按要求工作,我在特定的纬度和经度坐标上添加了一堆球体。视图从应该显示的内容向左/向右(向东/向西)镜像。我花了几个小时尝试调整相机的不同排列。



下面是我完整的测试应用视图控制器。我无法找出正确的更改组合来正确渲染场景。北极的球体是正确的。以我当前的经度从北极延伸到赤道的球面线出现在预期的上方。这是其他不正确的领域。它们是从东西向镜像的,就好像镜子是在我自己的经度上。



如果您想测试此代码,请使用以下命令创建一个新的Game项目: SceneKit。将模板GameViewController.swift文件替换为以下模板。您还需要将隐私-使用中的位置说明键添加到Info.plist。我还建议调整... 行中lon的,以便数字以您自己的经度开头或结尾。然后,您可以查看是否在显示的正确部分上绘制了球体。这可能还需要对 UIColor(色调:CGFloat(lon + 104)* 2 / 255.0 颜色)稍作调整。

 导入UIKit 
导入QuartzCore
导入SceneKit
导入CoreLocation
导入CoreMotion

let EARTH_RADIUS = 6378137.0

类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

函数弧度(_度:Double)-> Double {
返回度* Double.pi / 180
}

函数度(_弧度:Double)-> Double {
返回弧度* 180 / Double.pi
}

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

scnCameraArm.eulerAngles.y =浮点数(弧度(偏航角))
scnCameraArm.eulerAngles.x =浮点数(弧度(螺距))
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)
}

//标记:-UIViewController方法

覆盖func viewDidLoad(){
super.viewDidLoad()

//创建新场景
let scene = SCNScene()

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

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

场景.rootNode.addChildNode(scnCameraArm)

//创建一个环境光并将其添加到场景中
让contextLightNode = SCNNode()
environmentalLightNode.light = SCNLight()
EnvironmentalLightNode.light!.type = .ambient
EnvironmentalLightNode.light!.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)

//检索SCNView
让scnView = self.view为! SCNView

//将场景设置为视图
scnView.scene =场景
//scnView.pointOfView = scnCamera

//在上方绘制球体西半球的一部分
,代表跨步大步(从:0,至:-105,由:-15){
代表lat跨步大步(从:0,至:90,乘:15) {
let mat4 = SCNMaterial()
如果lat == 90 {
mat4.diffuse.contents = UIColor.yellow
}否则,如果lat == -90 {
mat4.diffuse.contents = UIColor.orange
} else {
//mat4.diffuse.contents = UIColor(红色:CGFloat(lat + 90)/ 255.0,绿色:CGFloat(lon + 104) * 4 / 255.0,蓝色:1,阿尔法:1)
mat4.diffuse.contents = UIColor(色相:CGFloat(lon + 104)* 2 / 255.0,饱和度:1,亮度:CGFloat(255-拉特* 2)/ 255.0,alpha:1)
}

让球= S CNSphere(半径:100000)
ball.firstMaterial = mat4
let ballNode = SCNNode(geometry:ball)
ballNode.position = SCNVector3(x:0.0,y:0.0,z:Float( 100000 + EARTH_RADIUS))

让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)))
}
}

//配置视图
scnView.backgroundColor = UIColor(red:0,green:191/255,蓝色:255/255,alpha:1)//天蓝色

locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
let auth = CLLocationManager.authorizationStatus()
开关auth {
case .authorizedWhenInUse:
locationManager.startUpdatingLocation()
case .not确定:
locationManager.requestWhenInUseAuthorization()
默认值:
休息
}

motionManager = CMMotionManager()
motionManager.deviceMotionUpdateInterval = 1/30
motionManager.startDeviceMotionUpdates(使用:.xTrueNorthZVertical ,至:OperationQueue.main){(错误,运动)在
中,如果error == nil {
如果让运动=运动{
// print( pitch:\(self。度(motion.attitude.roll * self.pitchAdjust)),横摇:\(self.degrees(motion.attitude.pitch * self.rollAdjust)),偏航:\(self.degrees(-motion.attitude.yaw )))
self.scnCamera.eulerAngles.z =浮动(motion.attitude.yaw + self.yawAdjust)
self.scnC amera.eulerAngles.x =浮动(motion.attitude.roll * self.pitchAdjust)
self.scnCamera.eulerAngles.y =浮动(motion.attitude.pitch * self.rollAdjust)
}
}
}
}

覆盖func viewWillAppear(_ Booled动画){
super.viewWillAppear(动画)

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

覆盖var shouldAutorotate:Bool {
return false
}

覆盖var preferredsStatusBarHidden:Bool {
返回true
}

覆盖varsupportedInterfaceOrientations:UIInterfaceOrientationMask {
return .landscape
}

//标记:-CLLocationManagerDelegate方法

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

func locationManager(_ manager:CLLocationManager,didUpdateLocations location:[CLLocation]){
for loc在位置{
print(loc)
如果loc.horizo​​ntalAccuracy> 0&& loc.horizo​​ntalAccuracy< = 100 {
setCameraPosition(loc:loc)
}
}
}

func locationManager(_ manager:CLLocationManager,didFailWithError错误:错误){

}
}

更改可能需要结合使用 setCameraPosition 方法和/或 motionManager.startDeviceMotionUpdates 在结束时进行组合 viewDidLoad

解决方案

感谢Hal提供的有关创建的非常有用的建议我场景中的一个 scn文件。在场景编辑器中查看了该场景之后,我意识到一切都左右反转了,因为偏航欧拉角(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)))


I'm attempting my first SceneKit app. My goal is to simulate a view from the surface of the Earth and being able to point the device's camera in any direction and overlay information over the camera view.

To start, I'm simply trying to get the SceneKit camera view to match the device's orientation. To verify that it is working as desired, I am adding a bunch of spheres at specific latitude and longitude coordinates.

Everything is working except for one important issue. The view is mirrored left/right (east/west) from what it should be showing. I've spent hours trying different permutations of adjusting the camera.

Below is my complete test app view controller. I can't figure out the right combination of changes to get the scene to render properly. The sphere at the North Pole is correct. The line of spheres stretching from the North Pole to the equator at my own current longitude appears overhead as expected. It's the other lines of spheres that are incorrect. They are mirrored east/west from what they should be as if the mirror is at my own longitude.

If you want to test this code, create a new Game project with SceneKit. Replace the template GameViewController.swift file with the one below. You also need to add the "Privacy - Location When In Use Description" key to the Info.plist. I also suggest adjusting the for lon in ... line so the numbers either start or end with your own longitude. Then you can see whether the spheres are being drawn on the correct portion of the display. This might also require a slight adjustment to the UIColor(hue: CGFloat(lon + 104) * 2 / 255.0 color.

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) {

    }
}

The changes probably need to be made in some combination of the setCameraPosition method and/or the motionManager.startDeviceMotionUpdates closure at the end of viewDidLoad.

解决方案

Thanks to Hal for his very helpful suggestion for creating an "scn" file from my scene. After viewing that scene in the scene editor, I realized that everything was reversed left/right because the yaw Euler angle (Y-axis) rotation was reverse of what I thought. So when I set the node's eulerAngles.y to the desired longitude, I was rotating in the opposite direction of what I wanted. But since the camera had the same error, the spheres at my location were in the correct location but everything else was reversed left-to-right.

The solution was to negate the longitude value for both the spheres and the camera location.

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

needs to be:

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

And:

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

needs to be:

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

这篇关于SceneKit视图向后呈现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆