从四元数获得类似指南针的行为
Getting compass-like behavior from quaternion
假设你有一个相机投影矩阵,即相机平移向量+旋转四元数,就像每个典型的相机一样,它可以向任何方向移动和旋转。无论它是向前看、向上看还是向下看,它的旋转都是独立的,我需要显示一个类似指南针的仪表,指向相机的目标位置。
问题是,当相机指向下方时,相机围绕其光学中心的旋转定义了罗盘的值,但当相机指向前方时,相机围绕其中心的旋转不再影响罗盘,在这种情况下,相机的方向定义了罗盘的值。
当相机仅向下倾斜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;
}
以下是如何获取世界中指南针的方向space:
在 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;
cameraWorldDirXZ
和 targetWorldDirXZ
之间的角度就是您的罗盘针的角度。
但我认为这不会像您想象的那样。这为您提供了围绕 y
轴旋转 camera.forward
矢量以面向目标所需的角度。如果围绕 camera.forward
旋转,则不会更改 camera.forward
矢量或 y
轴,因此罗盘不会改变。
您可能想在本地尝试使用指南针 space。为此,您投影到相机 XZ 平面上:
Vector3 cameraLocalDirXZ = camera.forward;
Vector3 targetLocalDirXZ = Vector3.ProjectOnPlane(target.position, camera.up).normalized;
同样,cameraLocalDirXZ
和 targetLocalDirXZ
之间的角度是罗盘指针的角度。这为您提供了围绕 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;
假设你有一个相机投影矩阵,即相机平移向量+旋转四元数,就像每个典型的相机一样,它可以向任何方向移动和旋转。无论它是向前看、向上看还是向下看,它的旋转都是独立的,我需要显示一个类似指南针的仪表,指向相机的目标位置。
问题是,当相机指向下方时,相机围绕其光学中心的旋转定义了罗盘的值,但当相机指向前方时,相机围绕其中心的旋转不再影响罗盘,在这种情况下,相机的方向定义了罗盘的值。
当相机仅向下倾斜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;
}
以下是如何获取世界中指南针的方向space:
在 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;
cameraWorldDirXZ
和 targetWorldDirXZ
之间的角度就是您的罗盘针的角度。
但我认为这不会像您想象的那样。这为您提供了围绕 y
轴旋转 camera.forward
矢量以面向目标所需的角度。如果围绕 camera.forward
旋转,则不会更改 camera.forward
矢量或 y
轴,因此罗盘不会改变。
您可能想在本地尝试使用指南针 space。为此,您投影到相机 XZ 平面上:
Vector3 cameraLocalDirXZ = camera.forward;
Vector3 targetLocalDirXZ = Vector3.ProjectOnPlane(target.position, camera.up).normalized;
同样,cameraLocalDirXZ
和 targetLocalDirXZ
之间的角度是罗盘指针的角度。这为您提供了围绕 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;