摄像阳极随iOS设备移动而旋转



这是一个360度视频播放器项目。

我在根节点上增加了一个摄像阳极(SCNNode),摄像阳极被放在SCNSphere的中心(0,0,0),它现在可以播放视频了。

现在我必须使用devicemotion。当设备移动时,我需要旋转摄像机。不只是旋转某个角度。(不只是设备移动,当我拿着一个设备时,我的移动就像设备在移动。因为我发现如果我使用deviceMotion.attitude.roll,相机阳极只在设备自行移动时移动,而不是当我用设备绕圈时移动

当设备位于(x1,y1,z1)位置时,相机阳极会随着设备移动而旋转,当设备再次位于(x1,y1,z1)位置时,视图应该与上次离开时相同。

这是我所做的:

if (self.motionManager.gyroAvailable) {
        self.motionManager.gyroUpdateInterval = 1.0/60.0;
        [self.motionManager startGyroUpdatesToQueue:self.queue withHandler:^(CMGyroData *gyroData, NSError *error){
            if (error) {
                [self.motionManager stopGyroUpdates];
                NSLog(@"Gyroscope encountered error:%@",error);
            }else {
                CGFloat tempX = 0;
                CGFloat tempY = 0;
                CGFloat tempZ = 0;
                if ((fabs(gyroData.rotationRate.y)/60) > 0.002) {
                    tempY = gyroData.rotationRate.y/60;
                }
                tempX = gyroData.rotationRate.x/60;
                tempZ = gyroData.rotationRate.z/60;
                [self.cameraNode runAction:[SCNAction rotateByX:-tempY y:tempX z:tempZ duration:0]];
            }
        }];
    }else {
        NSLog(@"This device has no gyroscope");
    }

数学可能是错误的,我不知道为什么我要划分60,但当我使用[self.cameraNode runAction:[SCNAction rotateByX:-tempY y:tempX z:tempZ duration:0]];

时,它似乎是我需要的最接近的值

问题是什么?

  1. 我应该使用哪些数据?

CMGyroDataCMDeviceMotion。如果使用CMDeviceMotion,我应该使用哪个特定的值?deviceMotion.attitude, deviceMotion.attitude.quaternion, deviceMotion.gravity

  • 这是正确的方法来旋转相机阳极?有很多方法,我不确定。
  • + (SCNAction *)rotateByX:(CGFloat)xAngle y:(CGFloat)yAngle z:(CGFloat)zAngle duration:(NSTimeInterval)duration;

    + (SCNAction *)rotateToX:(CGFloat)xAngle y:(CGFloat)yAngle z:(CGFloat)zAngle duration:(NSTimeInterval)duration;

    设备运动是最好的,因为它连接了多个传感器,并以一种有意义的方式将数据融合在一起。最好避免使用欧拉角,因为它们会受到万向节锁定的影响。相反,使用四元数并使用它们设置相机方向。

    我已经为CMDeviceMotion创建了一个Swift类扩展,它可以让你确定设备正在看什么(对于屏幕的任何方向)。

    从那里,很容易旋转你的相机。首先,设置你的设备运动(在视图中显示,或其他适当的位置):

    if motionManager.deviceMotionAvailable {
        motionManager.deviceMotionUpdateInterval = 0.017
        motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue(), withHandler: deviceDidMove)
    }
    

    然后在您的deviceDidMove实现中处理这些更新,使用前面提到的CMDeviceMotion扩展:

    func deviceDidMove(motion: CMDeviceMotion?, error: NSError?) {
        if let motion = motion {
            yourCameraNode.orientation = motion.gaze(atOrientation: UIApplication.sharedApplication().statusBarOrientation)
        }
    }
    

    现在你的相机应该跟随你的设备在空间的方向

    Swift 5.0/iOS 13 Update

    使用最初答案的辉煌扩展,您可以简单地这样做:

    override func viewDidAppear(_ animated: Bool) {
        // let motionManager = CMMotionManager() // definition made globally
        
        // CoreMotion
        motionManager.startDeviceMotionUpdates()
        motionManager.deviceMotionUpdateInterval = 1.0 / 60.0
        
        let interfaceOrientation = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.windowScene?.interfaceOrientation // for iOS13 and later
        
        // .xTrueNorthZVertical // always aligns to the real north
        // .xArbitraryZVertical // will use this one for initial direction
        motionManager.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: OperationQueue.main, withHandler: { (motion: CMDeviceMotion?, err: Error?) in
            guard let m = motion else { return }
            // self.cameraNode.orientation = m.gaze(atOrientation: UIApplication.shared.statusBarOrientation) // before iOS 13
            self.cameraNode.orientation = m.gaze(atOrientation: interfaceOrientation!) // for iOS13 and later
        })
    }
    

    对于iOS 15使用扩展从初始答案

    override func viewDidAppear(_ animated: Bool) {
        // let motionManager = CMMotionManager() // definition made globally
    // CoreMotion
    motionManager.startDeviceMotionUpdates()
    motionManager.deviceMotionUpdateInterval = 1.0 / 60.0
    //this following line is changed to avoid deprecated term 
    let interfaceOrientation = keyWindow!.windowScene?.interfaceOrientation
    // .xTrueNorthZVertical // always aligns to the real north
    // .xArbitraryZVertical // will use this one for initial direction
    motionManager.startDeviceMotionUpdates(using: .xArbitraryZVertical, to: OperationQueue.main, withHandler: { (motion: CMDeviceMotion?, err: Error?) in
        guard let m = motion else { return }
        // self.cameraNode.orientation = m.gaze(atOrientation: UIApplication.shared.statusBarOrientation) // before iOS 13
        self.cameraNode.orientation = m.gaze(atOrientation: interfaceOrientation!) // for iOS13 and later
    })
    }
    extension (change name UIApplication) {
    var keyWindow: UIWindow? {
        // Get connected scenes
        return UIApplication.shared.connectedScenes
            // Keep only active scenes, onscreen and visible to the user
            .filter { $0.activationState == .foregroundActive }
            // Keep only the first `UIWindowScene`
            .first(where: { $0 is UIWindowScene })
            // Get its associated windows
            .flatMap({ $0 as? UIWindowScene })?.windows
            // Finally, keep only the key window
            .first(where: .isKeyWindow)
    }
    

    相关内容

    • 没有找到相关文章

    最新更新