type
status
date
slug
summary
tags
category
icon
password
FootPlacement节点应该是基于与Half-Life:Alyx一样的原型,也就是基于rune skovbo的locomotion system硕士论文实现的,在cpp的1841行的注释中有标记算法引用
- 对于Plant的定义:指的是提供支撑的腿。踩在地上并且提供重心支撑的腿为Planted Leg
- 对于腿和骨盆的运行时信息(FLegRuntimeData\FPelvisRuntimeData)。各自定义了特有的BoneData\InputPoseData\InterpolationData,仅传入对应所需的信息
- BoneData用于原始传入,传入为诸如FCompactPoseBoneIndex这种包含复杂数据结构的类,由FBoneReference在InitializeBoneReference()通过传入FBoneContainer后由GetCOmpactPoseIndex获取得到
- 实际计算时会类似原生算法将关键节点先提取其Transform信息(FTransform)然后进行计算
- 通过GatherLegDataFromInputs()将BoneData中的FCompactPoseBoneIndex信息通过Context转换为FTransform
- 转换于动画蓝图中SkeletonControl公用的EvaluateSkeletalControl函数执行,该函数用于评估受影响骨骼的新组件空间变换
- 对于腿数据结构,多了一个PlantData的信息以控制落点
- 对于骨盆数据结构,则更为精简
- UE的方案额外考虑
- 不同类型锁死脚部旋转的选项EFootPlacementLockType,取决于球关节或者踝关节的Plant状态
- 骨盆高度通过不同方式倒算,其位置受作为支撑的腿的影响,不同EPelvisHeightMode模式将以不同的条件判断产生支撑的腿是哪条以产生不同的结果
- EActorMovementCompensationMode提供了更多选项用以下半身产生快速移动时上半身是完全跟随还是有一定惯性的跟随,主要用于垂直高度的变化
- FFootPlacementPelvisSettings.HorizontalRebalancingWeight 为角色在迈开腿后的重心平衡提供了骨盆移动的补充,值控制移动强度,以获得更接近正确的姿势(但是可能造成模型相对碰撞盒偏移太多导致整体超出碰撞盒)
- FFootPlacementPelvisSettings.MaxOffsetHorizontal增加了一个水平移动距离的限制,在限制内优先锁死支撑脚的旋转,在限制之外优先进行支撑脚的旋转以在更大步长的移动时腿部尤其是踝关节的自然变换
- 腿数据中InputPoseData部分
- 存在FootToGround与BallToGround两个Transform,代表踝关节与球关节位置向地面的投射交点。目前这两个数据仅用于Debug,后续会用于增加精度
- 存在AlignmentAlpha但是在特定环境不适用,将会在有预测或相位信息时引入roll-phase alpha
- 腿数据中PlantData部分
- 存在AlignmentAlpha但是在特定环境不适用,将会在有预测或相位信息时引入roll-phase alpha
- PlantResult
- 将添加求关节和踝关节的Transform用于程序性的脚趾滚动和臀部修正
- FootPlacementLockType中,将增加通过检测球关节或踝关节是否种在地上判断和选择旋转轴的选项
- FootPlacementTraceSetting中,将增加ground normal的无法tracing 的fallback方案
- 在速度大于固定脚到地面的阈值但是小于脱离的阈值时,代表脚侧倾阶段,将设置新的参数与算法用于提升这段动画的违和感
- AlignPlantToGround() 也就是将脚种在地上的判定,当 当前动作具有不止一个目标平面(比如上楼)时,效果是有问题的
TODO部分
AnimNode_FootPlacement
├─ TODO: 后续支持非垂直朝下的Approach方向
├─ TODO: 初始化骨骼引用时,腿部数据中BallToGround和FootToGround的赋值目前只考虑
│ 了平地,没考虑斜坡等情况
├─ 数据收集
│ ├─ GatherPelvisDataFromInputs
│ │ └─ TODO: PelvisData相关参数目前逐帧赋值(考虑到可能的动态变化),
│ │ 但是目前并不需要动态变化其实可以初始化时执行一次就结束
│ └─ GatherLegDataFromInputs
│ ├─ TODO: PlantRuntimeSettings相关参数目前逐帧赋值(考虑到可能的动态变化),
│ │ 但是目前并不需要动态变化其实可以初始化时执行一次就结束
│ └─ CalcTargetPlantPlaneDistance
│ └─ TODO: Real FootBase/BallBase:真实的脚底距离
│ └─ 当前计算是基于 Foot和Ball骨骼的TransformPosition到目标平面的最近距离
│ 实际上应该是脚底到平面的距离(骨骼位置一般会比脚底更靠上一点)
├─ 状态处理
│ ├─ ProcessCharacterState
│ └─ CalculateFootMidpoint
├─ 逐脚部对齐ProcessFootAlignment
│ ├─ DeterminePlantType
│ │ └─ TODO: Test along approach direction
│ ├─ (UpdatePlantingPlaneInterpolation) FindPlantTraceImpact
│ │ └─ TODO: 目前简单与复杂碰撞结果混合依赖传入Alpha,后续应通过预测结果调整
│ └─ AlignPlantToGround
│ ├─ TODO: Handle non-flat surfaces (e.g., stairs):非平面的ik参考距离
│ │ └─ 当前参考距离基于IKFootRoot节点计算Plane,两条腿通用。但是诸如楼
│ │ 梯等环境下,两条腿并不适用于同一个Plane
│ └─ TODO: adjust the foot vertically:垂直调整脚部
│ └─ 旋转脚可能发生剪切,垂直调整避免剪切
├─ 骨盆求解
│ ├─ SolvePelvis
│ │ ├─ TODO: Cache this(?)
│ │ ├─ TODO: Improve foot roll accuracy:提升脚旋转效果
│ │ │ └─ 当IK脚水平移动到FK脚的位置时,移动距离约为脚长,会导致Heel在锁定时
│ │ │ 且Hips下垂前被抬起
│ └─ UpdatePelvisInterpolation
└─ 最终调整
├─ FinalizeFootAlignment
│ ├─ TODO: CorrectedFootLocationCS应当通过脚绕脚趾滚动,并反向调整脚趾旋转以得到
│ │ 更精确的位置
│ ├─ TODO: 将IK骨骼拉向FK骨骼(但是需要先解决脚不过度伸展时导致的不连续性问题)
│ ├─ TODO: Consolidate with CalcTargetPlantPlaneDistance 这里也需要获取Foot和Ball相对
│ │ 平面的距离,CalcTargetPlanePlaneDistance函数有相关功能,需要规整代码
│ └─ TODO: 输出之前还需要调整ball关节和hip关节,相关功能都还没做
└─ ApplyBoneTransforms
UE方案流程
流程
总体调用入口是
EvaluateSkeletalControl_AnyThread
调用流程:
AnimNode_FootPlacement
├─ 数据收集
│ ├─ GatherPelvisDataFromInputs:获取骨盆初始姿态
│ └─ GatherLegDataFromInputs:获取腿部骨骼链长度与速度曲线
├─ 状态处理
│ ├─ ProcessCharacterState:执行地面检测与速度方向分析
│ └─ CalculateFootMidpoint:计算多脚支撑中心点
├─ 脚部对齐
│ ├─ FindPlantTraceImpact:执行地面追踪
│ ├─ AlignPlantToGround:将脚部旋转对齐地面法线
│ └─ DeterminePlantType:判定脚步锁定状态
├─ 骨盆求解
│ ├─ SolvePelvis:优化多腿约束
│ └─ UpdatePelvisInterpolation:应用弹簧平滑
└─ 最终调整
├─ FinalizeFootAlignment:防止腿部过度伸展
└─ ApplyBoneTransforms:应用最终骨骼变换
核心算法函数
FindPelvisOffsetRangeForLimb() 函数
- 根据若干传入信息获得脚底面空间信息,脚底、踝关节位置信息
- LimbLength:骨骼链的长度
- DesiredExtension:输入姿势脚到髋直线距离
- MaxExtension
- 直线距离大于骨骼链的长度时,等于直线距离
- 直线距离更短时,等于直线距离 + 距离差 * MinExtensionRatio
- 依据踝关节相对目标脚底平面的位置信息得到HipHeight后,进行状态判断
- 踝关节位置高于目标平面(脚底平面空间)
- 先根据 脚长 和 目标位置与脚在目标平面投影位置的距离差 ,依照HeelLiftRatio进行一次偏移确认
- 然后根据紫色线段插值再进行一次修正
- 然后执行原文臀部调整部分(以下是差异)
- 原作不论是desired还是max位置,都是基于原始脚部位置(上图黑色的PlantTargetLocationCS)画圆得到的交点。但是在UE方案中,圆心不同。两者的圆心为 已修正后PlantTargetLocationCS(上图绿色的PlantTargetLocationCS)以两者长度为LegLength参数经FindPlantLocationAdjustedByOrtogonalLimit()函数再次调整后的结果
- 额外获取了期望位置和骨骼链长度两者中更小的值计算了一个MinExtension,以根据脚的高度限制髋关节的调整,避免出现脚部高度过于接近臀部的姿势(表现上较差,比如此时容易导致膝盖部分蒙皮穿插)
- 在 期望落点DesiredPlantTargetLocasionCS 沿 目标平面FootPlane 的 法线方向-Context.ApproachDirCS 按MinExtension偏移后的坐标,与当前髋关节位置的插值向量在法线方向上的投影减去产生Plant的距离阈值后的插值就是Pelvis的MinExtension
- 踝关节低于目标平面——不做调整


AlignPlantToGround

- 获取当前脚位置与目标动画脚位置,依照目标动画脚位置InputPostFootTransformWS与目标动画地面IKFootRootPlaneWS的相对距离IKFootRootToFootRootTargetDistance,反推当前脚位置InOutFootTransformWS相对当前目标平面PlantPlaneWS的落点CorrectedPlaneIntersectionWS经与目标动画位置等量抬起后对应的悬浮点CorrectedLocationWS作为目标调整结果

- InputPoseAlignedRotationWS是InputPoseFootTransformWS应用平面差的变换PlanePlaneDeltaRotation得到的旋转,其参考系是在世界空间
- UnalignedIKFootToUnalignedInputPoseRotationDelta是将InOutFootTransformWS的旋转转换成相对InputPoseAlignedRotationWS的结果,参考系是InputPoseAlignedRotationWS
- (第一次修正:整个Foot插值Swing但完全应用Twist) - 在UnalignedIKFootToUnalignedInputPoseRotationDelta这个变换中,由于最后修正值不一定转到目标角度,但是为了匹配脚部和最终平面,需要调整扭转,因此通过ToSwingTwist取到这个过程的扭转值单独对InputPoseAlignedRotationWS进行扭转修正得到变换AlignedRotationWS,再得到InOutFootTransformWS相对AlignedRotationWS的变换AlignedToUnalignedRotationDelta
- (第二次修正:对Twist按固定参数进行比例应用) -再一次将AlignedToUnalignedRotationDelta分解出扭转项,使用Slerp和参数AnkleTwistReduction进行重新插值得到一个强度减弱的扭转,应用到AlignedRotationWS上得到最终应该应用在InOutFootTransformWS的旋转变换TwistCorrectedRotationWS
- 原目标是InputPostFootTransformWS,重新匹配平面后的目标是InputPoseAlignedRotationWS,期待变换是由InOutFootTransformWS变化为InputPoseAlignedRotationWS所产生的变换,但是经过一系列修正与平衡后实际应用的是TwistCorrectedRotationWS
- 作者:Reguluz
- 链接:https://reguluz.cn/article/1eb65fbc-2b71-80d3-9c4f-f0f9b3e51d24
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章