Final IK 文档手册译文


Final IK 文档手册译文

/*全身BipedIK

Final IK 为采用biped骨骼的角色提供了一个极端灵活、强大且高效的全身IK解算器。FBBIK先把Biped角色映射到一个低解析度的多反应器角色IK绑定,解算它,然后重新把结果映射角色。这个过程在LateUpdate进行,在Mecanim/Legacy动画完成之后,所以它跟animator系统完全分离。

链:

内部,每个肢体和躯干都是FBIKChain类的实例。躯干是根链,只包含一个node,躯干是它的子链。这种设置形成一个围绕根链的多反应器IK树。

节点:

node是链的组成部分。比如,手臂链包括3个节点-上臂、前臂、手。每个节点维护一个到骨骼的引用(node.transform)。当解算器运行或者结束后,解算出的骨骼位置会保存在node.solverPosition。

反应器:

FBBIK有3种反应器-【末端反应器】(手、脚),【中间反应器】(肩膀、大腿)和【多反应器】(躯干)。当旋转【中间反应器】和【多反应器】无效的时候可以转动【末端反应器】。旋转【末端反应器】同样会改变躯干的弯曲方向(除非你用弯曲目标覆盖它)。躯干反应器是一个【多反应器】,意味著它拖动2个大腿反应器(简化躯干定位)。反应器有positionOffset属性可以非常简单的操控下层动画。反应器会在每次解算结束之后将postionOffset重置为Vector3.zero。

拉和伸:

每个链都有pull属性。当pull=1,pull权重跟肢体一样。这意味著你触到所有反应器是没有保障的,太远的会接触不到。可以通过修改reach参数,或增加解算迭代次数,或这每帧多次解算,来调整或者改进这个结果。但是,如果你把左臂链的pull权重设置到1然后其他都为0,那么你可以拉角色的左手到无限远而不丢失联系。

映射:

IKSolverFullBodyBiped解算一个非常低分辨率的快速躯壳。虽然你的角色有多得多的脊椎骨,他可能有两倍的骨骼在手臂和肩膀或者臀部骨骼等等。因此,在解算之前,解算器需要映射高分辨率的骨架到低分辨率的解算骨架,在解算完成之后再逆向操作。有3种映射器,1.IKMappingSpine用来映射盆骨和脊柱;2.IKMappingLimb映射躯干;3.IKMappingBone映射头部;你可以通过IKSolverFullBody.spineMappingIKSolverFullBody.limbMappings和IKSolverFullBody.boneMappings访问他们。

限制:

1.FullBodyBipedIK没有头部反应器。因为头部只有一个骨头,你可以再FullBodyIK结束之后随意转动,而且,只有极少的情况你需要抓住头部拉动角色。即使如此,它可以通过拉动肩膀来很好的模拟。这是一个让程序更快、更稳定的优化措施。

2.FullBodyBipedIK没有手指和脚趾反应器。解算手指IK似乎有点过分使用IK了,因为极少有游戏对手指做绑定。使用104段CCD或者FABRIK链控制手指,可能这样浪费宝贵的毫秒数并不是你想要的。你可以参考DrivingRigdemo了解如何快速摆手指到物体上。

3.FullBodyBipedIK采样角色的初始pose(在Start()函数,当每次你重新初始化解算器的时候)找到以什么方式弯曲肢体。从此,限制-角色的肢体需要被弯曲到自然的方向。有些角色可能是处于T-Pose,他们的肢体是伸直的。有些角色的肢体可能还轻微的往反向弯曲(有些Mixamo绑定的角色)。FullBodyBipedIK会警告你这些问题可能发生。你需要手动在SceneView稍微旋转骨骼到正确的弯曲方向。因为,这些旋转会在播放的时候被动画覆盖,所以你不用担心搞坏你的角色。

4.FullBodyBipedIK 没有肘部、膝部反应器。这功能在将来如果确实需要的话可能增加。仍然支持通过弯曲目标改变肘部、膝盖的位置。

5.Optimize Game Objects 需要关闭,或者至少所有解算器需要的骨骼要引入(FullBodyBipedIK.references)。

6.支持只有扭曲动画的额外骨骼。如果额外骨骼有摆动动画,比如翅膀摆动。那么FBBIK将不能正确解算。

7.FullBodyBipedIK 需要角色开启animatePhysics。这是Mecanim系统的一个bug,它不允许运行时改变animatePhysics,那么FullBodyBipedIK需要根据初始animatePhysics进行刷新。

8.FullBodyBipedIK不会在拉动手的时候旋转肩膀。这样可以维护胸部动画的。大多数情况,这没有问题,但有时候,特别是抓取或者有东西高于头部的时候,让肩膀跟著旋转会更真实。这种情况,你不仅需要一个底层的抬起动画,还需要肩膀骨骼在IK解算器读取pose之前,能用脚本旋转它。这里同样有一个脚本包括这个demo,它叫ShoulderRotator。

9.当你移动【末端反应器】并且反应器权重=0,FBBIK将在动画的时候尝试修复肢体的弯曲方向,当肢体旋转接近180度的时候你将体验到肢体的轰响,就是说,解算器无法知道在这个奇点上如何旋转。比如,你有一个行走动画,手下垂,而你想要够到头顶正上方的某个物体,这时候无论你做反应器旋转动画或者使用弯曲目标,要在接近180度生硬旋转中确保手臂不翻转都会很麻烦。这并不是一个bug,如果我们想要保持默认动画弯曲方向,这在逻辑上是必然会发生的。

10.FullBodyBipedIK认定手肘和膝盖关节是铰链关节,就是说前臂不能相对上臂扭动。在最常见的绑定,像3dsMaxbiped上这没问题,因为这些绑定阻止这类动画出现。然而如果绑定允许这种扭曲。这不会导致FBBIK解算器失效,相反,FBBIK解算器会强制手肘和膝盖约束到铰链关节,即使它没有反应器。这不会改变肢体末端,它会轻微的改变肢体弯曲方向,和骨骼的扭曲。

开始教程:

1.添加FullBodyBipedIK组件到角色根节点(与Animator/Animation组件相同)

2.确认自动检测到的biped references正确。

3.确认跟节点正确检测到。它应该是脊柱下端的骨骼。

4.看下SceneView,确认FBBIK骨架在角色上显示。

5.按Play,操控解算器。

*/

//访问反应器:

public FullBodyBipedIK ik;

void LateUpdate () {

ik.solver.leftHandEffector.position = something;// 设置左手反应器位置(世界坐标)。如果把权重设置到0,反应器会没有效果。

ik.solver.leftHandEffector.rotation = something;// 设置左手反应器旋转(世界坐标)。如果把权重设置到0,反应器会没有效果。

ik.solver.leftHandEffector.positionWeight = 1f;// 反应器位置权重,这里设为1,左手位置会被固定到ik.solver.leftHandEffector.position.

// 反应器旋转权重,这里设为1,左手和手臂旋转会被固定到ik.solver.leftHandEffector.rotation.

// 注意:如果你想要旋转手,同时不改变手臂绑定

// 最好在FBBIK更新结束之后,再直接旋转手(使用委托事件OnPostUpdate)

ik.solver.leftHandEffector.rotationWeight = 1f;

// 使手便宜它的动画位置,如果反应器的positionWeight为1, 它没有作用。

// 注意:在反应器每帧更新完之后他会重置positionOffset 到 Vector3.zero所以你必须另外设置它.

// This enables you to easily edit the value by more than one script.

ik.solver.leftHandEffector.positionOffset += something;

// 反应器模式用来改变肢体行为方式,当没有参与的时候。The effector mode is for changing the way the limb behaves when not weighed in.

// Free 表示node完全有solver决定。Free means the node is completely at the mercy of the solver.

// 如果你碰到动作平滑问题,你可以试试把手的模式改为MaintainAnimatedPosition 或 MaintainRelativePosition

// MaintainAnimatedPosition在每次迭代解算的时候,重置node到骨骼动画位置

// 这对脚非常有用,因为一般你需要他们在动画位置上。

// MaintainRelativePositionWeight 维护相关部位的相对位置,胸部相对手臂,臀部相对腿。maintains the limb's position relative to the chest for the arms and hips for the legs.

// 所以,如果你从左手拉动角色,右手臂会随著胸部运动。

// 一般你不需要把这个行为应用到腿部。

ik.solver.leftHandEffector.maintainRelativePositionWeight = 1f;

// 躯体反应器是一个【多反应器】,表示在解算器里它跟其他node一起操控,明确的说是左右大腿。

// 所以,你可以带著大腿骨骼移动躯体反应器。如果我们设置effectChildNodes 为 false,大腿node就不会被躯体反应器改变。

ik.solver.body.effectChildNodes = false;

// 其他反应器:rightHandEffector, leftFootEffector, rightFootEffector, leftShoulderEffector, rightShoulderEffector,leftThighEffector, rightThighEffector, bodyEffector

// 你也可以通过下面的方法找到反应器:

ik.solver.GetEffector(FullBodyBipedEffector effectorType);

ik.solver.GetEffector(FullBodyBipedChain chainType);

ik.solver.GetEndEffector(FullBodyBipedChain chainType);// 值返回手或脚反应器

}

// 访问链:

public FullBodyBipedIK ik;

void LateUpdate () {

ik.solver.leftArmChain.pull = 1f;// 改变左臂pull值

ik.solver.leftArmChain.reach = 0f;// 改变左臂Reach值

// 其他链:rightArmChain, leftLegChain, rightLegChain, chain (根链)

// 你可以用下面的方法找到链:

ik.solver.GetChain(FullBodyBipedChain chainType);

ik.solver.GetChain(FullBodyBipedEffector effectorType);

}

//访问映射:

public FullBodyBipedIK ik;

void LateUpdate () {

ik.solver.spineMapping.iterations = 2;// 改变脊柱映射迭代次数

ik.solver.leftArmMapping.maintainRotationWeight = 1f;// 使左手处理旋转与动画保持一致。Make the left hand maintain it's rotation as animated.

ik.solver.headMapping.maintainRotationWeight = 1f;// 使头部旋转处理与动画一致。Make the head maintain it's rotation as animated.

}

//运行时添加 FullBodyBipedIK (UMA):

using RootMotion;// 需要先包含RootMotion 命名空间,因为BipedReferences类

FullBodyBipedIK ik;

// 任何时候可以调用下面的方法

// 注意,FBBIK初始化的时候要采样角色pose,所以再调用这个方法的时候肢体需要被弯曲到自然方向

void AddFBBIK (GameObject go, BipedReferences references = null) {

if (references == null) { // 还没有定义biped的时候,自动检测它

BipedReferences.AutoDetectReferences(ref references, go.transform, BipedReferences.AutoDetectParams.Default);

}

ik = go.AddComponent<FullBodyBipedIK>();// 添加组件

// 设置FBBIK到references. 第二个参数可以为空(root node) 如果你信任FBBIK自动检测到根节点在正确的脊柱骨骼上

ik.SetReferences(references, null);

}

//优化FullBodyBipedIK:

//如果角色没有显示,可以使用renderer.isVisible来显示

//大部分时间你不需要这么的solver迭代和脊椎映射迭代。注意: 如果只有一次迭代,角色的肩膀和大腿拉动手和脚的时候可能想脱臼

//如果不需要“Reach”值,请保持它为0。它默认=0.05f来提高精度。

//保持Spine Twist 权重=0。如果你不需要他。

//同样设置"Spine Stiffness", "Pull Body Vertical" and/or "Pull Body Horizontal" =0 可以提高性能。

//肢体:

// LimbIK 继承 TrigonometricIK 来定义3段 类似手臂和腿的肢体。

// LimbIK由以下几个修Bend改器组成:

// Animation: 尝试按动画控制弯曲方向

// Target: 根据目标IKRotation旋转弯曲方向

// Parent: 根据父物体旋转弯曲方向(盆骨或锁骨)

// Arm: 保持在生物统计学的松弛位置弯曲手臂(相对上面的,它的消耗更大)

// 如果所有的bend修改器都不适合你的要求,那么你可以添加弯曲目标,简单如下;

using RootMotion.FinalIK;

public LimbIK limbIK;

void LateUpdate () {

limbIK.solver.SetBendGoalPosition(transform.position);

}

// 这会使limb弯曲到 从第一个bone指向goal位置的方向

// IKSolverLimb.maintainRotationWeight 属性允许操控最后一个骨骼,使它保持在肢体解算器前的世界左边旋转值

// 当你需要复位脚的时候这非常有用,但This is most useful when we need to reposition a foot, but maintain it's rotation as it was animated to ensure proper alignment with the ground surface.

// 开始:

// 添加LimbIK 组件到角色跟物体(角色需要朝前)

// 添加骨骼到LimbIK组件 bone1, bone2 and bone3

// 按play

// 用脚本实现:

public LimbIK limbIK;

void LateUpdate () {

// 改变目标位置、旋转、权重

limbIK.solver.IKPosition = something;

limbIK.solver.IKRotation = something;

limbIK.solver.IKPositionWeight = something;

limbIK.solver.IKRotationWeight = something;

// 改变自动bend修改器

limbIK.solver.bendModifier = IKSolverLimb.BendModifier.Animation;// 按动画控制弯曲方向

limbIK.solver.bendModifier = IKSolverLimb.BendModifier.Target;// 按目标旋转控制弯曲

limbIK.solver.bendModifier = IKSolverLimb.BendModifier.Parent;// 根据父骨骼控制弯曲方向(盆骨、肩膀)

// 尝试按生物学松弛状态控制手臂弯曲方向

// 腿部不会受这个影响

limbIK.solver.bendModifier = IKSolverLimb.BendModifier.Arm;

}

//运行时添加LimbIK:

//通过脚本添加

LimbIK.solver.SetChain()

//旋转限制:

//所有旋转约束和其他FinalIK组件都是基于Quaternion(四元数)和Axis-Angle(轴角)以确保一致性,连续性和最小化奇异问题。FinalIK没有包含简单欧拉角选项。

//所有旋转约束基于local rotation 并且像Physics 关节一样使用初始local rotation 作为引用。这使它轴独立,并容易设置。

//所有旋转约束可以在SceneView 可以undo

//所有旋转约束支持与IK解算器联合工作

//角度mode

//简单的角度摆动和扭动限制

//铰链mode

//铰链旋转限制限制限制关节只绕某个轴旋转一定角度。它旋转值可以超过360度。

//多边形mode

// 使用一个球面多边形来限制旋转范围,这是普遍存在的球窝使关节。A reach cone is specified as a spherical polygon on the surface of a a reach sphere that defines all

// positions the longitudinal segment axis beyond the joint can take.

// twist limit 参数定义了围绕主轴最大扭转角度

// 这个类基于论文:

// "Fast and Easy Reach-Cone Joint Limits"

// Jane Wilhelms and Allen Van Gelder. Computer Science Dept., University of California, Santa Cruz, CA 95064. August 2, 2001

// 多边形角度限制模式提供方便快速编辑SceneView工具,来编辑、克隆和修改reachcone点

//样条线mode

// 使用样条线限制普遍的球窝关节的旋转范围

// 通过AnimationCurve正交投影到球面获得光滑、快速的限制范围。

// twist limit 参数定义了围绕主轴最大扭转角度

// 样条线角度限制模式提供方便快速编辑SceneView工具,来编辑、克隆和修改reachcone点

//扩展Final IK

// FinalIK的IK解算器和旋转限制架构基于可扩展思想搭建。

// FinalIK的一些组件比如BipedIK, 本质上仅仅是IK解算器的收集器

// 自定义IK组件:

// 在你发掘出FinalIK全部能力之前,了解一些它的架构非常重要。

// IK组件和IK解算器之间的区别:

// 架构上,IK解算器类包括反向关节功能,而IK组件只是拥有、初始化、更新他的解算器然后提供SceneView操作手柄和自定义inspector。

// 因此,IK解算器完全独立于他的组件,并且可以完全脱离直接引用来使用:

using RootMotion.FinalIK;

public IKSolverCCD spine = new IKSolverCCD();

public IKSolverLimb limb = new IKSolverLimb();

void Start() {

// 基于多种原因,根transform引用在IK解算器初始化的时候引用。

// 启发式解算器(CCD) IKSolverCCD, IKSolverFABRIK and IKSolverAim 只需要用它作为警告log的上下文。

// 角色解算器 IKSolverLimb, IKSolverLookAt, BipedIK and IKSolverFullBodyBiped 用它来定义相对与角色的方向。

// IKSolverFABRIKRoot 使用它作为所有FABRIK链的根

spine.Initiate(transform);

limb.Initiate(transform);

}

void LateUpdate() {

// 按顺序更新IK解算器

// 在多IK解算器拥有骨骼层次,先初始化的它的父物体比较好。

spine.Update();

limb.Update();

}

// 你现在有了一个自己的IK组件

// 如果你想把自己的功能全部放在单个组件里,像 BipedIK,所以你不会管理很多不同的IK组件在你的场景里

// 写自定义旋转限制:

// 所有旋转限制都继承抽象类RotationLimit 构建自己的类同样需要继承这个基类,并覆盖抽象方法。

protected abstract Quaternion LimitRotation(Quaternion rotation);

// 这个方法里你需要应用约束,并返回输入Quaternion

// 注意:比较重要,Quaternion已经转换为物体的默认的local rotation空间,意味著如果你返回Quaternion.identity,物体会总是会被修正到他的初始local rotation

// 下面的代码是一个创建自定义旋转限制的模板:

using RootMotion.FinalIK;

// 声明类并继承RotationLimit.cs

public class RotationLimitCustom: RotationLimit {

// 在实例Transform的local空间限制旋转

protected override Quaternion LimitRotation(Quaternion rotation) {

return MyLimitFunction(rotation);

}

}

// 新旋转限制由所有可约束的IK解算器认可并自动应用。

// 组合IK组件:

// 当创建更复杂的IK系统时,你可能需要完全控制解算器的更新顺序。要这么做,你只要禁用他们的组件,并用外部脚本管理他们的解算器

// 所有IK组件继承类IK,而所有IK解算器继承抽象类IKSolver。这让你很容易的掌控和替代解算器,而不需要知道特别的解算器类型

// 控制多重IK组件的更新顺序

using RootMotion.FinalIK;

// IK组件数组,你可以从inspector赋值

// IK是抽象类,所以你不用使用的是考虑那个特别的IK组件类型 is abstract, so it does not matter which specific IK component types are used.

public IK[] components;

void Start() {

// 禁用所有其他IK组件,这样他们不会更新解算器。使用 Disable() 代替=false 后者不能保证初始化。

foreach (IK component in components) component.Disable();

}

void LateUpdate() {

// 按顺序更新IK解算器

foreach (IK component in components) component.GetIKSolver().Update();

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容