You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过俯仰角(pitch)、偏航角(yaw)和滚转角(roll)计算向上向量(up vector)?

从方向向量+Roll计算Up向量并构建四元数

嘿,这个需求其实是3D姿态计算里的常见场景,我来一步步给你捋清楚,保证每一步都落地:

首先咱们先明确几个基础定义:

  • Forward向量:就是你手里的方向向量,先确保它是单位向量(如果不是,先做归一化:forward = normalize(your_direction_vector)
  • 世界Up向量:默认用(0, 1, 0)就行,除非你用了自定义的世界坐标系,那得换成对应的参考向上向量。

第一步:计算无Roll时的初始Up向量

我们需要先得到一个和Forward严格垂直的初始Up向量,这里要注意一个特殊情况:如果Forward和世界Up几乎共线(比如抬头看天、低头看地),直接叉乘会出问题,得换个参考向量。

具体操作:

  1. 先算Right向量
    • 如果Forward和世界Up不共线(可以用点积判断:abs(dot(forward, world_up)) < 0.9999),就用right = cross(world_up, forward),然后归一化;
    • 如果共线,就用备用参考向量(1, 0, 0)来计算:right = cross((1,0,0), forward),再归一化。
  2. 得到初始Up向量:initial_up = cross(forward, right),再归一化。这时候initial_up和Forward完全垂直,且没有Roll旋转的影响。

第二步:用Roll旋转初始Up向量

Roll是绕Forward向量的旋转角,所以我们要把初始Up向量绕Forward轴旋转Roll角,得到最终的Up向量。

轴角旋转公式就能搞定,因为initial_up和Forward垂直,公式还能简化:

# 注意:roll要转成弧度,代码里别用角度!
final_up = initial_up * cos(roll) + cross(forward, initial_up) * sin(roll)

最后记得归一化final_up,避免浮点误差导致向量长度偏离1。


第三步:用Forward和Final Up构建四元数

现在我们有了正交的Forward和Final Up,先补全Right向量确保三个向量完全正交(消除计算误差):right = cross(final_up, forward),归一化。

接下来有两种常用的方式构建四元数:

方法1:从旋转矩阵转四元数

先把三个正交向量拼成旋转矩阵(右手坐标系下,列向量对应Right、Up、Forward):

M = [
    right.x, final_up.x, forward.x,
    right.y, final_up.y, forward.y,
    right.z, final_up.z, forward.z
]

然后用矩阵迹(trace)来计算四元数:

trace = M[0][0] + M[1][1] + M[2][2]
if trace > 0:
    s = sqrt(trace + 1.0) * 2
    q_w = 0.25 * s
    q_x = (M[2][1] - M[1][2]) / s
    q_y = (M[0][2] - M[2][0]) / s
    q_z = (M[1][0] - M[0][1]) / s
elif M[0][0] > M[1][1] and M[0][0] > M[2][2]:
    s = sqrt(1.0 + M[0][0] - M[1][1] - M[2][2]) * 2
    q_w = (M[2][1] - M[1][2]) / s
    q_x = 0.25 * s
    q_y = (M[0][1] + M[1][0]) / s
    q_z = (M[0][2] + M[2][0]) / s
elif M[1][1] > M[2][2]:
    s = sqrt(1.0 + M[1][1] - M[0][0] - M[2][2]) * 2
    q_w = (M[0][2] - M[2][0]) / s
    q_x = (M[0][1] + M[1][0]) / s
    q_y = 0.25 * s
    q_z = (M[1][2] + M[2][1]) / s
else:
    s = sqrt(1.0 + M[2][2] - M[0][0] - M[1][1]) * 2
    q_w = (M[1][0] - M[0][1]) / s
    q_x = (M[0][2] + M[2][0]) / s
    q_y = (M[1][2] + M[2][1]) / s
    q_z = 0.25 * s

最后归一化四元数:q = normalize(q_w, q_x, q_y, q_z)

方法2:直接用向量计算四元数

如果不想搞矩阵,也可以直接用三个正交向量计算:

q_w = sqrt(1 + right.x + final_up.y + forward.z) / 2
q_x = (final_up.z - forward.y) / (4 * q_w)
q_y = (forward.x - right.z) / (4 * q_w)
q_z = (right.y - final_up.x) / (4 * q_w)

如果q_w接近0(比如姿态接近翻转),就换用最大的分量作为起始计算,避免除以0,最后同样归一化。


几个关键注意点

  • 归一化!归一化!归一化! 每一步计算后都要归一化向量/四元数,浮点误差积累会让正交性彻底崩溃。
  • 坐标系一致性:左手/右手坐标系的叉乘顺序、旋转方向定义都不一样,一定要和你的项目统一。
  • Roll的方向:绕Forward顺时针还是逆时针为正?如果结果反了,把sin(roll)改成-sin(roll)就行。

内容的提问来源于stack exchange,提问作者PartyPlanet

火山引擎 最新活动