PAYATHON 2026

为什么相机转向 90 度后上下旋转会变成翻滚?

数据阿明

结论

问题出在旋转矩阵乘在 app.B 的哪一侧。按代码中的定义,app.B 的列表示相机各轴在世界坐标系中的方向,app.B.T 则把世界坐标转换到相机坐标。当前代码使用 R1.T @ app.B 和 R1 @ app.B,也就是左乘旋转矩阵,因此上下旋转始终绕世界坐标系的 x 轴进行。相机转向 x 轴后,固定的世界 x 轴会接近相机的前向轴,旋转看起来就像翻滚。

如果要让上下旋转绕相机自己的局部 x 轴,也就是当前相机的右轴,应将 R1 乘在 app.B 右侧:

# rotate camera around its local axes
if "up" in keys:
    app.B = app.B @ R1.T
if "left" in keys:
    app.B = app.B @ R2
if "down" in keys:
    app.B = app.B @ R1
if "right" in keys:
    app.B = app.B @ R2.T

这里沿用原代码中的矩阵和按键方向。R1 的转置决定上下方向,R2 的转置决定左右方向。如果实际转向与预期相反,交换对应矩阵及其转置即可。

为什么右乘会改变旋转轴

矩阵乘法的次序决定了旋转所在的坐标系。左乘 R @ B 会在世界坐标系中旋转相机基向量;右乘 B @ R 则会在相机局部坐标系中组合旋转。相机绕水平轴转向后,局部 x 轴也会改变方向,因此后续俯仰会沿着相机的右轴转动,不再绕固定的世界 x 轴。

代码中的坐标转换也体现了这一点:app.B.T @ (point - app.cxyz) 会将点变换到相机坐标;相机移动时使用 app.B[:, 0] 和 app.B[:, 2],分别沿相机局部的 x、z 轴移动。

如果左右转向必须保持世界竖直方向

上面的写法也会让左右旋转绕相机局部 y 轴进行,适用于自由转动的相机。如果希望左右转向始终绕世界 y 轴,例如保持地平线稳定,可以只将俯仰改为右乘,左右旋转仍使用左乘:

if "up" in keys:
    app.B = app.B @ R1.T       # 相机局部 x 轴
if "left" in keys:
    app.B = R2 @ app.B         # 世界 y 轴
if "down" in keys:
    app.B = app.B @ R1
if "right" in keys:
    app.B = R2.T @ app.B

这种混合方式让上下旋转跟随相机的右轴,左右旋转则继续绕世界竖直轴。如果需要限制俯仰角或避免欧拉角累积误差,通常需要单独保存 yaw 和 pitch,再按固定顺序重建相机朝向。当前问题先统一旋转轴所在的坐标系即可处理。

备注:内容仅供参考。