如何从前向向量计算俯仰和偏航值

How to compute pitch and yaw values from forward vector

我想根据相机在实例化时获得的位置和目标点来计算相机俯仰和偏航值。如果我将 pitch 初始化为 0,将 yaw 初始化为 -90(如 LearnOpenGL Camera Tutorial),当第一次旋转发生时,相机突然跳转;旋转正常后。因此,首先,从该教程中给出的方程式开始,我尝试从前向向量中获取俯仰和偏航值:

float pitch = glm::degrees(glm::asin(forward.y));
float yaw = glm::degreees(glm::acos((float)x/glm::cos(glm::asin(y))));

还有

float yaw = glm::degreees(glm::asin((float)z/glm::cos(glm::asin(y))));

并且cos(sin(y))不应该是0,所以y不应该是0或者-1。 我尝试实现这些,但偏航的 2 个值不相同,偏航的第一个值似乎是我正在寻找的,但俯仰不是。 之后,我尝试了一种简单的方法,知道 pitch 是前向矢量和 y 轴之间的角度,yaw 是前向矢量和 x 轴之间的角度,我尝试计算(在纸上)如下:

const glm::vec3 yUnit(0, 1, 0);
const glm::vec3 xUnit(1, 0, 0);
float pitch = glm::degrees(glm::acos(glm::dot(forward, yUnit)));
float yaw = glm::degrees(glm::acos(glm::dot(forward, xUnit)));

具有以下 2 个输入:position = (0, 0, 0),target = (0, 1, 2,5),forward = target - position = (0, 1, 2.5),归一化 forward ~ (0, 0.37, 0.926),结果是俯仰~68度,偏航=90度。 此外,我在应用程序中打印了俯仰和偏航值,预期值应为俯仰 = -20 和偏航 = -90。

如果我错了,你能解释一下吗?我错在哪里?

无法指向任何一行进行更改,但建议:

when the first rotate occurs, camera suddenly jumps

这表明某些东西没有按照您认为的那样进行初始化。在这种情况下,俯仰和偏航是可以从前向矢量计算得出的派生值,对吗?当你有派生值时,你不应该直接初始化它们,因为你有可能弄错。如果两个本应 "the same" 的值不同,就会发生奇怪的事情。而是初始化前向向量并立即从中计算俯仰和偏航。

And cos(sin(y)) should not be 0

我们都会时不时地犯这个错误。但在这种情况下,我认为这无关紧要。

但是,您可能想要测试前向向量为 (0, 0, 0) 时会发生什么。在图形编程中以某种方式获得全零向量是非常普遍的。

float yaw = glm::degreees(glm::acos((float)x/glm::cos(glm::asin(y))));

您决定使用哪种欧拉角表示了吗?在简单的情况下,您永远不需要使用多个单轴坐标值来计算角度,但在这里您使用了两个。

简单的情况是偏航(航向)、俯仰和滚转是独立的角度,因此如果俯仰改变,偏航不会改变,反之亦然。你最后一个带有 xUnit 和 yUnit 向量的代码块似乎就是这样做的。

然而,在飞行模拟器和航空航天计算中,偏航-俯仰-滚转有点复杂,因为它们不是独立的。偏航角可能是在俯仰平面内测量的,而不是绝对的 XZ。而航天偏航通常是从 "north" Z 轴而不是 X 轴测量的。所以你需要清楚你测量的是哪种偏航和俯仰,并且始终保持一致。并且您需要研究任何教科书示例或代码,以弄清楚他们如何使用俯仰-偏航-滚动以及它是否与您的一致。

我建议暂时坚持简单的单一坐标测量。

float pitch = glm::degrees(glm::acos(glm::dot(forward, yUnit)));

再一次,你确定前向标准化了吗? LookAt 通常被编码为比数学库更宽容,所以这是一个容易犯的错误。

并且,您是否检查过您的数学库对 -180 到 180 度以外的值有何作用?还要担心一件事。

希望这对您有所帮助。如果您发现欧拉角既繁琐又烦人,那么您并不孤单!这就是为什么许多 3D 书籍和教程都推荐学习四元数的原因。

根据您的预期值,您的俯仰角似乎应该在 -90 到 90 度之间变化,因此您应该使用 asin 而不是 acos 作为俯仰角。

计算偏航时,需要将前向矢量投影到xz平面上。您可以通过将 y 分量设置为零然后对其进行归一化来实现。

偏航需要在 0 到 360 度之间变化,但 acos 仅 returns 在 0 到 180 度之间变化。前 180 度的偏航是正确的,但随着偏航从 180 度增加到 360 度,acos 将从 180 度减少回零。 当前向向量与(0,0,1)的点积大于0时,yaw会大于180度,所以acos值需要用360减去来调整。

根据您的预期值,我猜测正确的偏航和俯仰值是

pitch = degrees(-asin(dot(forward, y_unit)))
forward.y = 0
forward = normalize(forward)
yaw = degrees(acos(dot(forward, x_unit)))
if(dot(forward, z_unit) > 0)
    yaw = 360 - yaw