如何从一个三维向量中获取偏航角、俯仰角和翻滚角

22

我有一个方向向量,将其应用于位置可以得到摄像机应该看的点。如何从中获取偏航、俯仰和翻滚角度以便正确使用glRotatef函数?

提前致谢。

5个回答

28

这些等式都不是“错误”的,但都有些笨拙。 Ryder052,你的示例没有考虑到一些情况,正如你所评论的那样。为什么不使用atan2呢?

给定单位(归一化)方向向量d

pitch = asin(-d.Y);
yaw = atan2(d.X, d.Z)

1
这个技巧对我很有用,如果我反转音高。正值让我向下看,而负值让我向上看。修复起来不难,但如果其他人遇到了这个问题。 - Christoffer
你好,我该如何在 -pi 和 pi 之间扩展 pitch? - camillo777
pitch = asin(-d.Y); 这行代码可能是错误的,因为对于 vec2(x,z) 来说,它的长度不一定是1。 - Liu Hao

21

从方向向量无法获取偏航角、俯仰角和翻滚角,因为方向向量只会指示要看哪个方向(偏航角和俯仰角)。

要获得偏航角和俯仰角,您需要使用三角学 - 我假设您具有一些工作知识。请查看此维基页面以获取一些有用的图表来可视化这些角度。

令Y = 偏航角,P = 俯仰角。

首先要获取偏航角:

tan(Y) = x/(-y)

现在要获得音高:

tan(P) = sqrt(x^2 + y^2)/z

为了获得Y和P的实际值,您需要使用反正切函数,我已经使用正切函数编写了它,以使推导更清晰。

请注意,负号取决于如何定义角度和轴线,但您应该能够理解。

然后,您可以将滚转设置为0或任何您喜欢的值。


1
但在实践中,任何应用程序通常都有一个“向上”的方向,您几乎总是可以选择一种方向,使“向上”尽可能接近向上。 - sigfpe
非常感谢回复。是的,你说得对,只有一个向量无法确定旋转,我需要一个上向量(如user207442建议的)或者一个标量来确定角度。我使用gluLookAt解决了我的问题,但我仍然很好奇如何获得这些角度。 - Jack
你对tan(Y)的表达方式更加直观易懂,但对于tan(P)来说就不是那么直观了。能否解释一下为什么这个表达式会得出tan(P),或者提供一个解释它的来源链接? - dinosaur
@dinosaur - tan(P) = distance-in-ground-plane / distance-along-up-axis. 如图所示,z 是沿着垂直轴的距离,sqrt(..) 是平面上二维向量(x,y)的距离。还要注意,在实际应用中,使用 P = arctan(whatever) 不会给出正确的一半角度的符号(必须单独计算符号,未显示);在实践中,使用 atan2。我认为这里应该是 P = atan2(z, distance-in-ground-plane)。如果我错了,请交换参数。 - ToolmakerSteve
嗯,仍然有问题,因为“距离”是无符号的;需要确定符号。必须在其他地方寻找完整的答案... - ToolmakerSteve

9

您可能并不需要偏航、俯仰和翻滚。您只需要正确的变换即可。尝试使用gluLookAt来构建它。文档


谢谢你的提示。我刚开始使用OpenGL,不知道这个函数。我用它解决了问题。 - Jack
这是一个不错的答案,但只适用于提供GLU的常见平台的子集(例如,不一定适用于OpenGL ES平台)。 - jheriko
@jheriko 很容易“自己动手”,几乎每本图形学教科书都有经典的例子。 - wcochran

4

pheelicks的方程式是错误的。亲爱的未来谷歌搜索者,这里有一个正确的方程式:

假设俯仰:绕X轴旋转,偏航:绕Y轴旋转,横滚:绕Z轴旋转。方向向量V(x,y,z)

pitch = asin(V.y / length(V));
yaw = asin( V.x / (cos(pitch)*length(V)) ); //Beware cos(pitch)==0, catch this exception!
roll = 0;

在我看来,它似乎是完全相同的东西,也许你应该检查一下三角函数。 - meneldal

0

嗯,我不确定这些答案在说什么,因为我无法让它们中的任何一个起作用。

我创建了自己的解决方案...

// get world target offset
// convert world target offset to world direction normal
// get my world transposed (inverted)
// rotate world direction normal to my space normal

Vector3D lWorldTargetOffset = gWorldTargetLocation - gWorldMyLocation;
Vector3D lWorldTargetDirection = lWorldTargetOffset.Normalize();
MatrixD lMyWorldTransposed = MatrixD.Transpose(MyWorldMatrix);
Vector3D lMySpaceTargetDirection = Vector3D.Rotate(lWorldTargetDirection, lMyWorldTransposed);

你现在在我的空间中有世界目标方向的法线

lMySpaceTargetDirection.X = pitch
lMySpaceTargetDirection.Y = yaw.
lMySpaceTargetDirection.Z = <0 infront >0 behind.

根据方向法线,所有值都在-1到1之间,因此如果您想要度数,只需* 90。

并不是说这是最好的解决方案,但这是我在花费数小时搜索和浏览大量晦涩难懂的内容后能够得到的唯一解决方案。

我希望您、某人或任何人都能够享受简单地旋转目标方向法线,使其相对于我的空间,并发现它易于使用且有用 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接