如何限制3D旋转(欧拉角)

6
什么是限制3D旋转(使用欧拉角和/或四元数)的正确/最佳方法?
看起来我的做法有些问题。我将旋转应用于动画骨架层次结构中的骨骼,有时骨骼会明显地“跳”到错误的方向,并且各个欧拉组件会绕到其范围的另一端。
我使用欧拉角表示当前方向,将其转换为四元数进行旋转,并独立夹紧每个欧拉角轴。以下是C++伪代码,基本上展示了我正在做的事情:
Euler min = ...;
Euler max = ...;

Quat rotation = ...;
Euler eCurrent = ...;

// do rotation
Quat qCurrent = eCurrent.toQuat();
qCurrent = qCurrent * rotation;
eCurrent = qCurrent.toEuler();

// constrain
for (unsigned int i = 0; i < 3; i++)
    eCurrent[i] = clamp(eCurrent[i], min[i], max[i]);

通常使用四元数更可取,因为它们不会受到欧拉角的限制(例如万向锁,这可能是您正在经历的)。每当需要进行复杂旋转时(例如在三个轴上),请使用四元数。欧拉角在简单旋转时也可以使用。 - user703016
我使用欧拉角的原因是为了能够对它们进行约束。我不知道如何约束四元数。 - KTC
请阅读http://www.geometrictools.com/Documentation/ConstrainedQuaternions.pdf。 - Charles Beattie
2个回答

1

欧拉角的一个问题是有多种方式来表示相同的旋转,因此您可以轻松地创建一系列平滑的旋转,但表示该旋转的角度可能会跳动。如果角度在受限范围内跳进跳出,则会看到您所描述的效果。

想象一下,只涉及X旋转,并且您已将X旋转限制在0到180度之间。还想象一下,将四元数转换为欧拉角的函数给出了-180到180度的角度。

然后您就有了这个旋转序列:

True rotation    After conversion    After constraint
179              179                 179
180              180                 180
181             -179                 0

您可以看到,尽管旋转平稳地变化,但由于转换函数强制结果在某个特定范围内表示,因此结果会突然从一侧跳到另一侧。

当您将四元数转换为欧拉角时,请找到最接近先前结果的角度。例如:

eCurrent = closestAngles(qCurrent.toEuler(),eCurrent);
eConstrained = clampAngles(eCurrent,min,max);

记住下一次的eCurrent值,并将eConstrained旋转应用于您的骨架。


我不明白。你似乎只是把问题重新表述为“closestAngles”的形式。 - KTC
重点是当你转换为欧拉角时,必须考虑先前的角度,以便你的旋转欧拉表示平滑。 - Vaughn Cato
好的,我明白你的意思。不过你确定每个轴都独立这样做是有效的吗?我尝试过,但从未成功过。它确实修复了“跳跃”,但骨骼仍然处于无效方向。我认为在欧拉角和四元数之间来回转换除了角度包裹问题外还有其他问题。 - KTC
@KTC:这取决于欧拉转换的作用。如果序列的中间旋转不能保证在-90度到+90度之间,那么你可能会遇到另一组等效角度。 - Vaughn Cato

0
问题在于您应用的约束与旋转无关。从概念上讲,这就是您要实现的内容:
- 假设骨骼处于未受约束状态。 - 应用旋转。 - 骨骼是否超出了约束范围?如果是,则将其旋转到不再受限制的位置。
您的代码通过夹紧欧拉旋转来将骨骼旋转回来。然而,此代码忽略了原始骨骼旋转,因此您会看到奇怪的行为,例如您所看到的抖动。
一个简单的解决方法是改为执行以下操作:
- 假设骨骼处于未受约束状态。 - 应用旋转。 - 测试骨骼是否超出了约束范围。 - 如果是,则需要找到约束停止运动的位置。 - 将旋转减半,反向应用旋转。 - 骨骼是否超出了约束范围?如果是,请返回步骤1。 - 如果没有,则将旋转减半,正向应用旋转。返回步骤2。 - 重复执行上述步骤,直到达到某个容差范围内的约束角度。

现在这样做是可行的,但由于你的旋转四元数被应用于所有角度,当其中任何一个约束被满足时,旋转将停止,即使其他地方还有自由。

如果相反地独立地应用旋转,则您将能够可靠地使用夹紧或上述技术来遵守约束,并尽可能接近目标进行旋转。 - -


在四元数和欧拉角之间进行转换似乎会破坏这个解决方案。我不确定如何在仍使用四元数的情况下独立应用旋转。 - KTC
我不能做到那样。我想使用四元数来在关键帧之间进行插值。 - KTC
请您详细说明一下,在尝试找到约束条件被触发的点时,您遇到了哪些问题? - Phil Martin
1
我有很多关节想要限制只能绕X轴和Y轴旋转,而不能绕Z轴滚动。但是,当我将旋转用四元数表示并转换回欧拉角时,似乎总会引入一些滚动,因此旋转总是超出其约束范围。 - KTC
关键帧满足约束条件。我从Blender导出约束和关键帧。但是我想在插值关键帧后进行IK处理,然后再进行约束。 - KTC
显示剩余2条评论

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