夹紧角度,结果连续

Clamp angle with continuous result

本文关键字:结果 连续      更新时间:2023-10-16

我正在编写自己的反向运动学,目前正在研究关节约束。这基本上很好,但我想从生成的动画中删除所有不连续性,而约束是最大的问题。

例如,假设你看到一个球在你的头上旋转。它向右旋转,你看起来向右,直到你的脖子扭曲极限。球仍然在移动,当它经过你的背部时,你的头会突然被夹在对面的极限上,向左看。这就是我们在典型的夹持中得到的,它给了我帧到帧的不连续性。

我需要的是,你的头会在几帧中从右向左移动。我的第一个想法是用我的X轴(肩膀之间)反射源方向,如果反射方向满足约束限制-返回。如果不满足限制-剪裁反射方向而不是源方向。

这应该给我一个良好而连续的运动,这不是一门火箭科学,但有很多细节需要处理。我的极限可能像min < max,也可能像max < min,它们可能通过切换角度值(像360->0180->-180)或不通过。此外,当我允许的范围大于180度时,逻辑应该略有不同。

不同案例的数量增长得很快,所以我想知道我是否能发现它已经在某个地方完成了?

BTW:如果虚幻引擎有任何不同,我会使用它。

编辑:

如果我用我的头脑例子误导了你,我很抱歉——这只是一个比喻,用来想象我的需求。我不会在最终动画中使用此代码来确定头部骨骼(或任何其他骨骼)的方向。我计划将其用作求解IK的一个非常基本的操作。它将被用于每个IK链的每次迭代,用于每个骨骼,多次。因此,它需要尽可能快。如果需要,所有更高层次的概念,如运动规划、动力学等,都将添加到另一层。

现在我只是要求一个函数,比如:

float clampAngle( float angle, float min, float max )

这将返回用于改变输入的连续值。

我目前正在处理不同的问题,但当我回到这个问题时,我会发布我的代码。

如果你把它看作一个模拟,它会清晰得多。您不需要任何计划,如您的评论中所述,因为下一帧仅使用当前帧上可用的信息进行计算。

如前两段所述,为颈部设置一个关节。当球转过来时,它会在一帧中从左向右翻转。

然后建立第二个相同的关节,并编写一个角度弹簧,随着时间的推移,它将朝着第一个关节旋转。通过调整弹簧系数——强度、阻尼等——你可以控制头部的旋转方式。

如何写出有角度的弹簧

这可能不是最好的方法,但代码非常简短和简单,所以这里。。。

让我们将这两个关节称为主关节和从关节。您需要将旋转phi和角速度omega存储在从属关节上。

phiomega是轴角矢量-一个三维矢量,其大小是围绕矢量定义的轴旋转的弧度数。它使模拟旋转变得非常容易。无论你的关节旋转是以欧拉角、矩阵还是四元数存储的,你可能会有一些类un-UE API来帮助提取轴/角度向量。

当主关节发生变化时,将其旋转转换为轴角度。让我们称之为phi_m

在开始帧上,从旋转phi应设置为与主旋转相同的值。CCD_ 11。它没有角速度,因此omega被设置为零矢量(0,0,0)

然后你向前移动一帧。帧的长度CCD_ 14可能是1/60秒或其他什么。

在模拟时,首先为phi_m计算一个新值。然后,主关节和从关节之间的差(矢量phi_m - phi)表示作用在从关节上的扭矩。角度越大,扭矩越大。假设质量为1.0,则使用F=ma,角加速度alpha等于扭矩。这意味着从关节在该帧上的角速度变化为alpha*dt。现在我们可以计算出新的角速度:omega = omega + (alpha*dt)。类似地,新的旋转是旧的旋转加上旋转随时间的变化(角速度)。phi = phi + (omega * dt)

把所有这些放在一起,加上弹簧strengthdamping的系数,模拟步骤可以是这样的:

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!!

一种方法:

  • 定义不能满足约束的区域(头部后面的圆锥体)
  • 测量目标进入该区域的距离(目标和圆锥体中心之间的角度除以半圆锥体角度)
  • 我们使用这个标量值来将受约束的对象平滑地混合回某个预定义的中性位置(即,当目标在头部后面移动时,将头部平滑地转回中心/静止位置)