Blending two animations

Started by
5 comments, last by a light breeze 3 years, 11 months ago

I have implemented basic animation into my engine and now I would like to blend two animations together. The way I have it set up is there are 

 

AnimtionClip

  • This is a serialized data containing its skeleton and bone information

AnimationInstance

  • Keeps track of how long it has been running, and the final transforms of the animation clip. Along with if it was active and if it is looping

AnimationController

  • Has an array of AnimationInstance's

 

Assuming that I wanted to blend two animations that had the same skeleton, I am not sure how I would. My first naive attempt was to take two AnimationInstance's and multiple their final transforms together. This didn't get quite what I was looking for, as they would blend but the character would double in size. Am I only supposed to multiple certain parts of the final transforms together?

Advertisement

Commonly this is done by taking two produced poses and blending between those. A basic start would be to lerp() between the two poses' bone transformations. Avoid doing this at the matrix level, but rather on the SRT level (scale, rotation, translation) of each bone in the skeleton.

For example:

finalSkelPose[boneIndex].translation = lerp(skelPose1[boneIndex].translation,skelPose2[boneIndex].translation, t);

Where t is your blend weight between 0 and 1.

Do the same for rotation (SLERP) and scaling (if you support it) then convert the final outcome to skin matrices.

Good luck!

I took a break from this as I kept having the same issue. From what I gather, this would be the correct way to blend two animations? The issue I am running into is that the bone positioning appears to be off so I wanted to check if the algorithm is correct
 


Matrix4x4 transforma = animationa->GetFinalTransforms()[i];
Matrix4x4 transformb = animationb->GetFinalTransforms()[i];

float amount = 0.5f;

Vector3 translationa(transforma._41, transforma._42, transforma._43);
Vector3 translationb(transformb._41, transformb._42, transformb._43);
Vector3 lerped = Vec3Lerp(translationa, translationb, amount);

finaltransforms[i].Identity();

Quaternion rotationa(transforma);
Quaternion rotationb(transformb);
Quaternion slerped = QuaternionSlerp(rotationa, rotationb, amount);

finaltransforms[i].Translate(lerped);
finaltransforms[i].RotateByQuaternion(slerped);

 

Bumping this old topic as I still run into this and I am not sure where to go to work out the issue

If this formula mixes two opposing bone directions at the same starting location, the end-point may end up in a strange location with a foot below ground. I would use inverse kinematics for arms and legs so that hands and feet have position and rotations interpolated first between animations. This also makes interaction with objects a lot easier. Then look at the distance between hand and shoulder to figure out where to rest the elbow (https://en.wikipedia.org/wiki/Pythagorean_theorem) along the middle circle based on its location on the previous frame and a recorded pose. Might raise the elbows while in a fight and lower them when crouching in a tight space. For more realistic walking, you might need to move the legs using artificial balance (like in Assassin's Creed) to let the legs follow the ground correctly.

My first thought when looking at your code is “order of operations”. Have you tried switching the last two statements around? Have you tried setting amount to 0 or 1 and checking if you can get back your original animations?

My second thought is that there is a needless round-trip from quaternion to matrix back to quaternion, because your animation system presumably uses quaterions internally for interpolation.

My third thought is that you're probably applying blending too late in your pipeline. You want to blend the local bone transforms, not the combined transforms generated by walking up the skeleton and definitely not the final transforms generated by multiplying the combined transforms with the inverse bind pose transforms.

This topic is closed to new replies.

Advertisement