转自:https://dawnarc.com/2018/05/ue4animationik-related/
Examples
工程1
在油管上看到一个UE4 IK动画的demo工程示例
该示例作者的主页:https://www.patreon.com/unrealcg
演示视频:Advanced foot IK for Unreal Engine 4 - (100% Free)
https://www.youtube.com/watch?v=XetC9ivIXFc
demo工程下载地址(4.19):
https://pan.baidu.com/s/1mlcM0cseKWpprnISVM0P5Q
工程2
该工程除了IK,还包括动画融合、物理等功能
演示视频:UE4 - Advanced Locomotion System V3 - Features
https://www.youtube.com/watch?v=yTniZCOCY7o
下载地址:Unreal商城,60刀
Advanced Locomotion System V3
https://www.unrealengine.com/marketplace/advanced-locomotion-system-v1
IK AnimNode
FABRIK (Forward And Backward Reaching Inverse Kinematics)
FABRIK
https://docs.unrealengine.com/en-us/Engine/Animation/NodeReference/Fabrik
Adding of a rifle and fabrik node to fix left hand
https://www.youtube.com/watch?v=49MJWjlSHcw
Look At
Look At
https://docs.unrealengine.com/en-us/Engine/Animation/NodeReference/SkeletalControls/LookAt
CCDIK (Cyclic Coordinate Descent Inverse Kinematics)
CCDIK
https://docs.unrealengine.com/en-us/Engine/Animation/NodeReference/SkeletalControls/CCDIK
Hand IK Retargeting
Hand IK Retargeting
https://docs.unrealengine.com/en-us/Engine/Animation/NodeReference/SkeletalControls/HandIKRetargeting
Two Bone IK
Two Bone IK
https://docs.unrealengine.com/en-us/Engine/Animation/NodeReference/SkeletalControls/TwoBoneIK
Spline IK
Spline IK
https://docs.unrealengine.com/en-us/Engine/Animation/NodeReference/SkeletalControls/SplineIK
IK Engine Source
CCDIK
Path:
Engine\Source\Runtime\AnimGraphRuntime\Private\BoneControllers\AnimNode_CCDIK.cpp
void FAnimNode_CCDIK::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) { const FBoneContainer& BoneContainer = Output.Pose.GetPose().GetBoneContainer(); // Update EffectorLocation if it is based off a bone position FTransform CSEffectorTransform = GetTargetTransform(Output.AnimInstanceProxy->GetComponentTransform(), Output.Pose, EffectorTarget, EffectorLocationSpace, EffectorLocation); FVector const CSEffectorLocation = CSEffectorTransform.GetLocation(); // Gather all bone indices between root and tip. TArray<FCompactPoseBoneIndex> BoneIndices; { const FCompactPoseBoneIndex RootIndex = RootBone.GetCompactPoseIndex(BoneContainer); FCompactPoseBoneIndex BoneIndex = TipBone.GetCompactPoseIndex(BoneContainer); do { BoneIndices.Insert(BoneIndex, 0); BoneIndex = Output.Pose.GetPose().GetParentBoneIndex(BoneIndex); } while (BoneIndex != RootIndex); BoneIndices.Insert(BoneIndex, 0); } // Gather transforms int32 const NumTransforms = BoneIndices.Num(); OutBoneTransforms.AddUninitialized(NumTransforms); // Gather chain links. These are non zero length bones. TArray<CCDIKChainLink> Chain; Chain.Reserve(NumTransforms); // Start with Root Bone { const FCompactPoseBoneIndex& RootBoneIndex = BoneIndices[0]; const FTransform& LocalTransform = Output.Pose.GetLocalSpaceTransform(RootBoneIndex); const FTransform& BoneCSTransform = Output.Pose.GetComponentSpaceTransform(RootBoneIndex); OutBoneTransforms[0] = FBoneTransform(RootBoneIndex, BoneCSTransform); Chain.Add(CCDIKChainLink(BoneCSTransform, LocalTransform, 0)); } // Go through remaining transforms for (int32 TransformIndex = 1; TransformIndex < NumTransforms; TransformIndex++) { const FCompactPoseBoneIndex& BoneIndex = BoneIndices[TransformIndex]; const FTransform& LocalTransform = Output.Pose.GetLocalSpaceTransform(BoneIndex); const FTransform& BoneCSTransform = Output.Pose.GetComponentSpaceTransform(BoneIndex); FVector const BoneCSPosition = BoneCSTransform.GetLocation(); OutBoneTransforms[TransformIndex] = FBoneTransform(BoneIndex, BoneCSTransform); // Calculate the combined length of this segment of skeleton float const BoneLength = FVector::Dist(BoneCSPosition, OutBoneTransforms[TransformIndex - 1].Transform.GetLocation()); if (!FMath::IsNearlyZero(BoneLength)) { Chain.Add(CCDIKChainLink(BoneCSTransform, LocalTransform, TransformIndex)); } else { // Mark this transform as a zero length child of the last link. // It will inherit position and delta rotation from parent link. CCDIKChainLink & ParentLink = Chain[Chain.Num() - 1]; ParentLink.ChildZeroLengthTransformIndices.Add(TransformIndex); } } // solve bool bBoneLocationUpdated = AnimationCore::SolveCCDIK(Chain, CSEffectorLocation, Precision, MaxIterations, bStartFromTail, bEnableRotationLimit, RotationLimitPerJoints); // If we moved some bones, update bone transforms. if (bBoneLocationUpdated) { int32 NumChainLinks = Chain.Num(); // First step: update bone transform positions from chain links. for (int32 LinkIndex = 0; LinkIndex < NumChainLinks; LinkIndex++) { CCDIKChainLink const & ChainLink = Chain[LinkIndex]; OutBoneTransforms[ChainLink.TransformIndex].Transform = ChainLink.Transform; // If there are any zero length children, update position of those int32 const NumChildren = ChainLink.ChildZeroLengthTransformIndices.Num(); for (int32 ChildIndex = 0; ChildIndex < NumChildren; ChildIndex++) { OutBoneTransforms[ChainLink.ChildZeroLengthTransformIndices[ChildIndex]].Transform = ChainLink.Transform; } } #if WITH_EDITOR DebugLines.Reset(OutBoneTransforms.Num()); DebugLines.AddUninitialized(OutBoneTransforms.Num()); for (int32 Index = 0; Index < OutBoneTransforms.Num(); ++Index) { DebugLines[Index] = OutBoneTransforms[Index].Transform.GetLocation(); } #endif // WITH_EDITOR } }
Path:
Engine\Source\Runtime\AnimationCore\Private\CCDIK.cpp
namespace AnimationCore { bool SolveCCDIK(TArray<CCDIKChainLink>& InOutChain, const FVector& TargetPosition, float Precision, int32 MaxIteration, bool bStartFromTail, bool bEnableRotationLimit, const TArray<float>& RotationLimitPerJoints) { struct Local { static bool UpdateChainLink(TArray<CCDIKChainLink>& Chain, int32 LinkIndex, const FVector& TargetPos, bool bInEnableRotationLimit, const TArray<float>& InRotationLimitPerJoints) { int32 const TipBoneLinkIndex = Chain.Num() - 1; ensure(Chain.IsValidIndex(TipBoneLinkIndex)); CCDIKChainLink& CurrentLink = Chain[LinkIndex]; // update new tip pos FVector TipPos = Chain[TipBoneLinkIndex].Transform.GetLocation(); FTransform& CurrentLinkTransform = CurrentLink.Transform; FVector ToEnd = TipPos - CurrentLinkTransform.GetLocation(); FVector ToTarget = TargetPos - CurrentLinkTransform.GetLocation(); ToEnd.Normalize(); ToTarget.Normalize(); float RotationLimitPerJointInRadian = FMath::DegreesToRadians(InRotationLimitPerJoints[LinkIndex]); float Angle = FMath::ClampAngle(FMath::Acos(FVector::DotProduct(ToEnd, ToTarget)), -RotationLimitPerJointInRadian, RotationLimitPerJointInRadian); bool bCanRotate = (FMath::Abs(Angle) > KINDA_SMALL_NUMBER) && (!bInEnableRotationLimit || RotationLimitPerJointInRadian > CurrentLink.CurrentAngleDelta); if (bCanRotate) { // check rotation limit first, if fails, just abort if (bInEnableRotationLimit) { if (RotationLimitPerJointInRadian < CurrentLink.CurrentAngleDelta + Angle) { Angle = RotationLimitPerJointInRadian - CurrentLink.CurrentAngleDelta; if (Angle <= KINDA_SMALL_NUMBER) { return false; } } CurrentLink.CurrentAngleDelta += Angle; } // continue with rotating toward to target FVector RotationAxis = FVector::CrossProduct(ToEnd, ToTarget); if (RotationAxis.SizeSquared() > 0.f) { RotationAxis.Normalize(); // Delta Rotation is the rotation to target FQuat DeltaRotation(RotationAxis, Angle); FQuat NewRotation = DeltaRotation * CurrentLinkTransform.GetRotation(); NewRotation.Normalize(); CurrentLinkTransform.SetRotation(NewRotation); // if I have parent, make sure to refresh local transform since my current transform has changed if (LinkIndex > 0) { CCDIKChainLink const & Parent = Chain[LinkIndex - 1]; CurrentLink.LocalTransform = CurrentLinkTransform.GetRelativeTransform(Parent.Transform); CurrentLink.LocalTransform.NormalizeRotation(); } // now update all my children to have proper transform FTransform CurrentParentTransform = CurrentLinkTransform; // now update all chain for (int32 ChildLinkIndex = LinkIndex + 1; ChildLinkIndex <= TipBoneLinkIndex; ++ChildLinkIndex) { CCDIKChainLink& ChildIterLink = Chain[ChildLinkIndex]; const FTransform LocalTransform = ChildIterLink.LocalTransform; ChildIterLink.Transform = LocalTransform * CurrentParentTransform; ChildIterLink.Transform.NormalizeRotation(); CurrentParentTransform = ChildIterLink.Transform; } return true; } } return false; } }; bool bBoneLocationUpdated = false; int32 const NumChainLinks = InOutChain.Num(); // iterate { int32 const TipBoneLinkIndex = NumChainLinks - 1; // @todo optimize locally if no update, stop? bool bLocalUpdated = false; // check how far const FVector TargetPos = TargetPosition; FVector TipPos = InOutChain[TipBoneLinkIndex].Transform.GetLocation(); float Distance = FVector::Dist(TargetPos, TipPos); int32 IterationCount = 0; while ((Distance > Precision) && (IterationCount++ < MaxIteration)) { // iterate from tip to root if (bStartFromTail) { for (int32 LinkIndex = TipBoneLinkIndex - 1; LinkIndex > 0; --LinkIndex) { bLocalUpdated |= Local::UpdateChainLink(InOutChain, LinkIndex, TargetPos, bEnableRotationLimit, RotationLimitPerJoints); } } else { for (int32 LinkIndex = 1; LinkIndex < TipBoneLinkIndex; ++LinkIndex) { bLocalUpdated |= Local::UpdateChainLink(InOutChain, LinkIndex, TargetPos, bEnableRotationLimit, RotationLimitPerJoints); } } Distance = FVector::Dist(InOutChain[TipBoneLinkIndex].Transform.GetLocation(), TargetPosition); bBoneLocationUpdated |= bLocalUpdated; // no more update in this iteration if (!bLocalUpdated) { break; } } } return bBoneLocationUpdated; } }