【问题标题】:Getting compass-like behavior from quaternion从四元数获得类似指南针的行为
【发布时间】:2017-12-30 17:21:40
【问题描述】:

假设你有一个相机投影矩阵,即相机平移向量+旋转四元数,就像每个典型的相机一样,它可以在任何方向移动和旋转。无论它是向前、向上还是向下看,无论它是旋转的,我都需要显示一个类似指南针的仪表,指向相机的目标位置。

问题是当相机指向下方时,相机围绕其光学中心的旋转定义了指南针的值,但是当相机指向前方时,相机围绕其中心的旋转不再影响指南针的值,在这种情况下,相机的方向定义了指南针的值。

当相机向下倾斜仅45度时会变得更难看,在这种情况下甚至不清楚围绕相机中心旋转是否会影响指南针的旋转。

那么有没有一种基于任意相机投影矩阵/四元数获取指南针值的优雅方法?

提前谢谢你!

【问题讨论】:

  • 这实际上看起来类似于很久以前没有答案的question I asked...
  • 因此,如果我理解正确,您有一个目标,并且您希望指南针显示相机朝向的方向与目标之间的角度差。指南针平面是否随相机转动还是正常总是指向上方?你的世界有明确的向上方向吗?
  • 我的世界有一个明确的up 方向,由y 轴定义。罗盘平面法向量始终平行于y,平面只能绕法向量旋转。但是当相机严格指向下方或上方时,相机围绕其中心的旋转会被考虑在内。这能回答你的问题吗?

标签: unity3d quaternions


【解决方案1】:

如果有人偶然发现这个问题,解决方案(感谢@Pluto)非常简单,将您的相机四元数乘以三个轴向量 (0,0,1)、(0,1,0)、(1,0 ,0),您将获得定义相机坐标系的三个向量,将这三个向量投影到您的平面上,找到您的三个投影点的质心,瞧,您就有了罗盘方向。

这是一段代码:

var rotation = /* Your quaternion */;

var cameraOrtX = rotation * new Vector3 (1, 0, 0);
var cameraOrtY = rotation * new Vector3 (0, 1, 0);
var cameraOrtZ = rotation * new Vector3 (0, 0, 1);

var cameraOrtPX = Vector3.ProjectOnPlane(cameraOrtX, new Vector3(0, 1, 0));
var cameraOrtPY = Vector3.ProjectOnPlane(cameraOrtY, new Vector3(0, 1, 0));
var cameraOrtPZ = Vector3.ProjectOnPlane(cameraOrtZ, new Vector3(0, 1, 0));

var centroid = (cameraOrtPX + cameraOrtPY + cameraOrtPZ) / 3.0f;

【讨论】:

    【解决方案2】:

    如果你只想要一个指向目标的箭头:

    Transform camera = Camera.main.transform;
    Transform target = Target.transform;
    Vector3 relativePosition = target.position - camera.position;
    
    Vector3 targetRelative = Vector3.ProjectOnPlane(relativePosition, camera.forward);
    
    float angle = Angle360(camera.up, targetRelative, camera.forward);  
    
    Compass.transform.rotation = Quaternion.Euler(0, 0, angle);
    

    角度函数为:

    float Angle360(Vector3 from, Vector3 to, Vector3 normal)
    {
        float dot = Vector3.Dot(from, to);
        float det = Vector3.Dot(normal, Vector3.Cross(from, to));
        return Mathf.Atan2(det, dot)*Mathf.Rad2Deg;
    }
    

    以下是在世界空间中获取指南针方向的方法:

    在XZ平面上投影相机方向和目标位置

    Transform camera = Camera.main.transform;
    Transform target = Target.transform;
    
    Vector3 cameraWorldDirXZ = Vector3.ProjectOnPlane(camera.forward, Vector3.up).normalized;
    Vector3 targetWorldDirXZ = Vector3.ProjectOnPlane(target.position, Vector3.up).normalized; 
    

    cameraWorldDirXZtargetWorldDirXZ 之间的夹角就是你的指南针的角度。

    但我认为这不会像你想象的那样。这为您提供了围绕y 轴旋转camera.forward 向量以面向目标所需的角度。如果您围绕camera.forward 旋转,则不会更改camera.forward 向量或y 轴,因此指南针不会改变。

    您可能想在本地空间尝试指南针。为此,您将其投影到相机 XZ 平面上:

    Vector3 cameraLocalDirXZ = camera.forward;
    Vector3 targetLocalDirXZ = Vector3.ProjectOnPlane(target.position, camera.up).normalized; 
    

    cameraLocalDirXZtargetLocalDirXZ 之间的角度再次是您的指南针的角度。这为您提供了围绕camera.up 旋转camera.forward 以面对目标所需的角度。请注意,当您围绕camera.forward 旋转时,它会改变camera.up,因此它会改变指南针方向。

    【讨论】:

    • 感谢@Pluto,您的解决方案让我有了一个想法,如果不是将一个向量投影到一个平面上,我会在 XY 或 YZ 平面上垂直投影两个向量,方法是选择一个具有最大幅度将提供相机方向的最大可能性
    猜你喜欢
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 2014-12-22
    • 2013-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-16
    相关资源
    最近更新 更多