我正在编写自己的反向运动学,目前正致力于联合约束。它很好,但我想从结果动画中删除所有不连续性,约束是最大的问题。
例如,让我们说你正在看着绕着你的头旋转的球。它向右旋转,你看起来正确,直到你的脖子扭曲限制。球仍在移动,当它经过你的背部时,你的头部会突然被相反的限制,向左看。这就是我们通过典型的夹紧方式得到的结果,它给了我框架到框架的不连续性。
我需要的是你的头在几帧内从右到左。我的第一个想法是用我的X轴(肩部之间)反映源方向,如果反射方向满足约束限制 - 返回它。如果它不符合限制 - 剪切反射方向而不是源方向。
这应该给我一个美好而持续的运动,它不是一个火箭科学,但有许多细节需要照顾。我的限制可能类似于min < max
,但也可能是max < min
,它们可能会通过切换角度值(例如360->0
或180->-180
)。此外,当我允许的范围大于180度时,逻辑应该略有不同。
不同案件的数量增长非常快,所以我想知道我是否能在某处找到它?
顺便说一句:如果它出现任何差异,我会使用虚幻引擎。
修改
对不起,如果我用我的头脑示例误导了你 - 这只是想象我的需求的类比。我不打算使用此代码来定位最终动画中的头骨(或任何其他骨头)。我计划将其用作解决IK的基本操作。它将在每个IK链中的每个迭代中用于每个骨骼,多次使用。所以它需要尽可能快。如果需要,所有更高级别的概念,如运动规划,动态等,都将添加到另一层。
现在我只想要一个像:
这样的功能float clampAngle( float angle, float min, float max )
将返回连续值以更改输入。
我目前处理不同的问题,但是当我回到这个问题时,我会发布我的代码。
答案 0 :(得分:1)
如果你认为这是一个模拟,它会更加清晰。您的评论中不需要任何计划,因为下一帧仅使用当前帧上可用的信息计算。
如前两段所述,为颈部设置一个关节。当球到来时,它将在一帧中从左向右翻转。
然后设置第二个相同的关节并写出一个角度弹簧,随着时间的推移将其旋转到第一个关节。通过调整弹簧系数 - 强度,阻尼等 - 您可以控制磁头旋转的方式。
如何写角弹簧?
这可能不是最好的方法,但代码非常简短,所以这里......
让我们调用2个关节主人和一个奴隶。您需要在从属关节上存储旋转phi
和角速度omega
。
phi
和omega
是manual update - 一个3d矢量,其大小是围绕由矢量定义的轴旋转的弧度数。它使模拟旋转变得非常容易。无论您的关节旋转是作为欧拉角或矩阵还是四元数存储,您都可能有一些联合UE API的类来帮助提取轴/角度向量。
当主关节改变时,将其旋转转换为轴角。让我们称之为phi_m
。
在起始帧上,从属旋转phi
应设置为与master相同的值。 phi = phi_m
。它没有角速度,因此omega
设置为零向量(0,0,0)
。
然后你前进一帧。帧长dt
可能是1/60秒或其他。
在模拟你的同时,首先计算出phi_m
的新值。然后主从和(矢量phi_m - phi
)之间的差异表示作用在从动关节上的扭矩。 axis-angle vectors。假设质量为1.0,则使用F = ma,角加速度alpha
等于扭矩。这意味着在该帧上从属关节的角速度变化为alpha*dt
。现在我们可以计算出新的角速度:omega = omega + (alpha*dt)
。类似地,新旋转是旧旋转加上随时间旋转的变化(角速度)。 phi = phi + (omega * dt)
将所有内容放在一起并添加弹簧strength
和damping
的系数,模拟步骤可以是这样的:
damping = 0.01 // between 0 and 1
strength = 0.2 // ..experiment
dt = currentTime-lastTimeEvaluated
phi_m = eulerToAxisAngle(master.rotate)
if (currentTime <= startTime || dt < 0) {
slave.phi = phi_m
slave.omega = (0,0,0)
} else {
alpha = (phi_m - slave.phi)*strength
slave.omega = (slave.omega * (1.0 - damping)) + (alpha*dt)
slave.phi = slave.phi + slave.omega*dt
}
slave.rotate = axisAngleToEuler(slave.phi)
lastTimeEvaluated = currentTime
请注意,阻尼只会消除一点存储的速度。如果阻尼非常低,则关节会过冲并振荡到位 - boing !!
答案 1 :(得分:0)
一种方法: