Unity Gyro Camera ---- 传感器控制摄像头旋转 + 正北校准 (纯原生支持Android+IOS,无需安装ARKit,ARCore等插件)

Unity Gyro Camera

传感器控制摄像头旋转 + 正北校准

纯原生支持Android+IOS,无需安装ARKit,ARCore等插件

这篇文章主要介绍如何利用手机原生的传感器,控制摄像头的旋转,最终可以实现AR或者VR的摄像头旋转控制

问题提出


 

  虽然,目前有一些用手机传感器控制虚拟摄像头旋转的方案,但是实际在一些应用中,需要手机的旋转和物理世界的是一样的。也就是说,如果手机朝着物理世界的正北方向,游戏里的场景也是朝着正北方向的。如果您不需要做正北的校准,那么可以忽略这篇文章,Unity官方文档就有实现方式。

目前情况


 

  虽然,目前网络上(无论国内还是国外)都有一些案例在Unity中利用传感器控制虚拟摄像头旋转的操作,但是总体来说存在以下的问题:

  1. 网上的方案都太旧了,适配Unity5或者IOS很早的版本
  2. 网上的方案层次不齐,有的方案写的代码太复杂了,最后化简下来都一样,但可能就看到一个复杂的方案,研究半天
  3. 目前的方案,在安卓下可以很好的解决问题(旋转和物理正北是一致的),但是在IOS的手机上却不成功,没有做正北的校准
  4. 网上又Unity Store中的方案,但是需要花钱购买(实际我购买后发现还是有问题的)
  5. 屏幕不同转向,结果不一样,有错误
  6. 或者需要安卓ARKit或者ARCore,ARFoundation等插件(如果要安装ARKit还需要申请摄像头访问权限,但实际上如果在VR的环境,完全不需要用到摄像头)

https://discussions.unity.com/t/how-can-i-make-the-camera-rotate-with-gyroscope-appropriately/220673

Unity gyroscope: Explained with code examples - VionixStudio

GitHub - Deankovitch/UnityGyroAccelCamera: Some way to make gyroscope and accelerometer work on all devices, in all Unity versions, using AHRS algoritm

Using gyroscope to control a camera? - Questions & Answers - Unity Discussions

[Sharing] Gyroscope Camera Script (iOS tested) - Unity Engine - Unity Discussions

Sensor Camera (Not AR): Gyroscope & Accelerometer | 镜头 | Unity Asset Store

GitHub - hbollon/GyroscopeControl: 🌀 Unity script used for smooth and customizable object rotation with gyroscope (initially configured to rotate x and z axis using x and y axis of gyro but can be easily edited). It include initial calibration with offset, rotation speed (Time.deltaTime * velocity), smoothing parameter editable in Unity inspector and debug overlay.

Quaternions applied to Sensor Fusion: gyro.attitude with compass - Questions & Answers - Unity Discussions

Match Unity camera with iPhone camera - Unity Engine - Unity Discussions

About attaching the unity camera to a gyro + magnetometer - Unity Engine - Unity Discussions

目标


 

    所以,我们要实现的目标是:

  1. 兼容安卓/IOS
  2. 有正北校准
  3. 无需安装多余插件
  4. 无需创建多余游戏物体,直接代码附加在要控制的组件(虚拟摄像头)
  5. 支持各种屏幕旋转方向

问题的产生


 

之所以,在安卓的环境下,不会有正北校准的问题,是因为Unity的接口: 

Input.gyro.attitude

本身已经融合了地磁的数据,所以无论手机在什么方向,都可以和物理的世界保持一致。但是IOS系统中,并没有做融合,需要手动操作。

虽然,我们可以通过:

Input.compass.trueHeading;
Input.compass.magneticHeading;

两个值,拿到当前物理正北的朝向,但是注意,这里返回的朝向是和屏幕方向密切相关的。它返回的值是,手机下方到上方的这条直线所在的方向,和物理世界的北极之间的角度。如果手机屏幕发生了旋转,最下方和最上方所在的轴并不是垂直与水平面的话,那么这个值是不准确的。

 例如上方的示意图,在手机完全水平(从下往上的轴在水平面的时候),这个时候的trueHeading或者magneticHeading的值,是和正北的夹角,但是当手机不是水平的时候(图例右侧),则这个角度(橙色的角)并不是真的正北朝向,而是蓝色角度,即手机从下往上的轴在水平面的投影与正北的夹角。

解决方案

综上所述,可以按照以下的代码完成最后的正北校准。

using UnityEngine;

public class GyroWithCompass : MonoBehaviour
{
    private double _lastCompassUpdateTime = 0;
    private Quaternion _correction = Quaternion.identity;
    private Quaternion _targetCorrection = Quaternion.identity;

    void Start()
    {
        Input.location.Start();
        Input.gyro.enabled = true;
        Input.compass.enabled = true;
    }

    void Update()
    {
        //当前陀螺仪的值
        Quaternion q = Input.gyro.attitude;
        //转到unity坐标系
        Quaternion gyro = new(q.x, q.y, -q.z, -q.w);
        //因为在AR/VR环境,我们希望手机是竖起来的,所以需要x轴旋转90度
        Quaternion gyroOrientation = Quaternion.Euler(90, 0, 0) * gyro;
#if UNITY_ANDROID
        transform.rotation = gyroOrientation;
#else
        //下面需要做正北校准
        //如果地磁计有新数据的时候
        if (Input.compass.timestamp > _lastCompassUpdateTime)
        {
            //存储刷新时间,以便判断是否有更新
            _lastCompassUpdateTime = Input.compass.timestamp;

            //根据水平仪(重力传感器,将手机从下网上的轴投影到水平面)
            Vector3 gravity = Input.gyro.gravity.normalized;
            Vector3 flatNorth = Input.compass.rawVector -
                Vector3.Dot(gravity, Input.compass.rawVector) * gravity;
            Quaternion compassOrientation = Quaternion.Inverse(Quaternion.LookRotation(flatNorth, -gravity));
            //转换到Unity坐标
            compassOrientation.z *= -1;
            compassOrientation.w *= -1;
            //计算最终正北校准需要的旋转四元数
            _targetCorrection = compassOrientation * Quaternion.Inverse(gyroOrientation);
        }
        //滤波
         _correction = Quaternion.Slerp(_correction,_targetCorrection,0.1f);
        //赋值
        transform.rotation = Quaternion.Euler(0, 180, 0) * _correction * gyroOrientation;
#endif
    }
}

 

posted @ 2024-08-05 21:23  Starsky星空一航  阅读(351)  评论(0)    收藏  举报