从四元数获得类似指南针的行为



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

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

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

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

提前谢谢你!

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

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(非常简单,将相机四元数乘以三个轴矢量(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;

最新更新