制作妇幼真实感的触手--逆向运动学教程 VR资源

小妞牛 2017-11-03 15:39:38
触手的逆向运动学 
 
这篇文章继续我们深入逆向运动学的旅程。你将学到怎样应用这个有力的技术来创造富有真实感的触手方法。
 
 
 
 
 
前言
 
 
在教程的前一章“机械手臂的逆向运动学”中,我们讨论了在机械手臂上怎样使用梯度下降来完成逆向运动学。这种由机械完成的运动,比复杂的人体关节简单得多。对于一个机械手臂而言,每个关节都由一个发动机控制。同理,对于一个生物手臂来讲,每块肌肉都是一个可以自行收缩和扩张的独立马达。
 
 
一些生物具备几个可以到达任意角度的关节,例如大象的鼻子、章鱼的触手等。将这些肢干模型化是一种挺特别的挑战,目前介绍的这些传统技术并不能产生非常真实的效果。
 
 
我们将从前一篇文章的例子开始,继续挑战,直到我们获得模型足够真实的行为为止。
 
 
触手绑定骨骼
 
 
当我们创建一个机械手臂之后,每个部分都是可独立移动的。相对而言,触手可以弯曲。这是一个我们无法忽略的基本特征,如果我们想要让它看起来真实的话。我们的触手需要能够弯曲。
 
 
一个可以允许这个基本特征的Unity组件叫做“Skinned Mesh Renderer”:
 
 
 
然而,Unity并不提供从编辑器创建Skinned Mesh Renderer的方法。一款3D建模软件,比如说Blender,此时就被需要了。下图展示了余下教程中将被使用的触手模型,里面有一系列明显的Bones骨骼),一个接着一个。它们是允许模型弯曲的Objects。
 
 
 
给模型加骨骼,也称为绑定Rigging),已经超出了本教程的范围。下面链接提供了比较好的骨骼绑定说明。(网址如下:https://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro/Bones)
 
 
骨骼和关节
 
 
给予触手逆向运动学的下一步是为每一个骨骼挂载一个RobotJoint脚本。通过这么做,我们赋予了我们的逆向运动学求解器一个使触手弯曲的能力。
 
 
对于普通的章鱼来说,每个“关节”都能够沿着它自身的三个轴向,自由的旋转。在“机械手臂的逆向运动学”中设计的代码,很可惜,只允许关节沿着单个轴向移动。改变这个代码对于已有脚本而言,将意味着把它们引入了一个新的复杂等级。反过来说,我们可以转动关节的轴向,使关节0转向X轴、关节1转向Y轴、关节2转向Z轴等等。这会导致不自然的行为,但是如果骨骼足够小,你就有可能不会感到有任何问题。
 
 
在随教程附带的可下载Unity工程文件中,这个名为SetRobotJointWeights的脚本会自动初始化触手所有关节的参数。或者,你也可以为了更好的控制每个骨骼的移动方式而自己来编写脚本。
 
 
缓和功能
 
 
下面的动画展示了两个触手。左边的那个抵着红色球体的触手使用的是“机械手臂的逆向运动学”中提出的算法,与之相反,右边的那个通过一个更加有机化的螺旋式运动来增加一个真实化的新扭曲。这个例子已经足够让我们明白为什么触手需要一个单独的教程。
 
 
 
 
两个触手都依赖梯度下降,不同之处在于它们试图最小化的误差函数。左边的机械触手只想碰到球,并不关心任何其他参数。一旦末梢执行器碰到了球,聚合度被满足,然后触手就会停止移动。
 
 
另一方面,右边的触手,正在最小化不同的函数。用于机械手臂的DistanceFromTarget类被一个新的更复杂的类替代了。我们能设计一个新的误差函数类来关注其他几个我们关心的参数。教程中展示的触手试着最小化三个不同的函数:
 
 
1.与目标的距离:已经定义好了。
 
2.末梢执行器的角度:在这条中,触手试着匹配我们想让物体达到的角度。这种行为能被看做上述动画,就是右侧触手围着球体螺旋式运动的样子。自从每个关节都有了一个移动的极限范围之后,这个范围限制将引发沿着骨骼链传播的连锁反应。我们可以强迫触手匹配物体试图达到的角度,通过这么做,我们可以测量末梢执行器和目标转角之间的角度。Unity有可以实现这项功能的便利函数:Quaternion.Angle
 
 
1
2
3
4
float rotationPenalty =
    Mathf.Abs
    (
         Quaternion.Angle(EndEffector.rotation, Destination.rotation) / 180f
    );
 
像这样匹配本地角度,可能并不总是个好主意。根据情况,你可能想让你的触手用不同的方式对齐。
 
3.扭曲:让肢干保持在一个不自然的位置看起来是十分不舒服的。这个参数使盘绕方案处于非常不利的境地,强制逆向运动学更线性、简单的旋转。为了计算扭曲的惩罚,我们首先要定义在我们的环境中什么是“扭曲”。定义它最简单的方法是作为所有关节角度的平均值。这种想让触手保持放松状态的惩罚方案,要求许多扭曲。
 
 
1
2
3
float torsionPenalty = 0;
for (int i = 0; i < solution.Length; i++)
     torsionPenalty += Mathf.Abs(solution);
torsionPenalty /= solution.Length;
 
这三种惩罚分数导致触手以更加真实的方式移动。更复杂的版本能确保它们即使当其他限制被完全满足时也保持晃动。
 
 
注意1:使用不同单位
 
那三个参数更有可能在不同的单位被表达。
 
当末梢执行器旋转的单位是角度时,与目标之间的距离可能是米。根据它们的价值重新测量它们很重要,便于不再衡量10度和距离目标10米的不同。
理想中,0和1之间应该被标准化。然后,可以使用系数来确定它们的相对重要性:
 
 
1
2
3
4
5
6
public float ErrorFunction (Vector3 target, float [] angles)
{
    return
        NormalisedDistance(target, angles) * DistanceWeight +
        NormalisedRotation(target, angles) * RotationWeight +
        NormalisedTorsion (target, angles) * TorsionWeight  ;
}
 
这个方法也给触手的良好行为提供了好的控制。它们可以根据情况随时被改变,以此来改变触手的移动方式。例如,你可以增加TorsionWeight类来解开触手凌乱的纠结。
 
 
注意2:我们没有分析定义!
 
 
正向运动学的典型性问题可以被模型化分析。这意味着我们可以展示一个问题并解决它。我们已经提到过了‘解决逆向运动学对偶问题的分析方法’的存在。
 
我们现在说的是一个函数,是一个可能的,不能被解析描述的一个函数。如果我们必须选择使用典型的可分析的方法来解决逆向运动学,我们就不能给我们的触手增加那些细微的差别了。使用梯度下降意味着我们几乎能够最小化任何随机函数,无论我们是否拥有它的方程式。
 
 
扩展
 
 
我们能做的改进扩展几乎没有限制。减速函数能够增加你触手的真实性,当触手越靠近目标时,触手就应该越慢。
 
同样的,触手不应该碰到自己。为了避免这个,可以给每个关节上加上精准碰撞器。然而,这会导致触手离奇的行为。代码忽略了碰撞,可能仍然会收敛一个发生自碰撞的方案。一个解决方法是修改适应函数(fitness function)便于自相交方案被高度处罚。
99VR视界二维码
热门推荐
Hot Recommended
在线客服