Animation Keyframes, Sampling, and Interpolation Questions

Started by
8 comments, last by L. Spiro 8 years, 11 months ago

Hi guys,

I'm having what seems to be a problem to me but may be normal behavior. Please take a look at this diagram:

bones_zps9yspojak.jpg

It's a two keyframe clip (of a leg.) At time t = 0 and at t = 1 the foot is exactly where it should be, meaning the same position since it should not move at all. However, during the interpolation it goes down a little then back up (see yellow arrow), it's largest deviation is at exactly t = 0.5. It's not out of control but it is noticable.

Here is what the numbers look like for the y-axis (up) value of the foot bone:

t = 0 : 0.5 (totally accurate)

t = 0.25 : 0.462 (sinking in to floor)

t = 0.5: 0.449 (at it's lowest)

t = 0.75 : 0.464 (coming back up)

t = 1.0 : 0.5 (totally accurate)

I tried both N-Lerp and Slerp, and made sure all my quaternions are normaized. I'm almost positive my math is correct.

The only way I can minimize the error is with more keyframes. That makes me wonder about something, if i specify those two keyframes in 3ds Max the other keyframes I would use would be interpolated by Max, so I'd really be using interpolated data for more data. What is Max doing that I'm not ?

Is this normal behavior ? It seems to creep in with IK; for example, if I make the foot go up I don't see any errors (I have to run the numbers to be sure though.)

Any information would be helpful. Thanks.

Advertisement

What is happening, is the heel and hip pulls toward each other. There could be a lot of reasons for this.

The first and most often is because it is being influenced by it's parent, you can test this by un-parenting the foot bone. If the bone remains in place after parented then the movement is inherited and not the fault of any mechanics.

The simple solution is to just add a extra key frame at the highest difference, this is a quick ugly solution that will increase resources.

The simplest and most efficient way of solving the problem, allow the bone above the foot to shrink a bit. This not only fixes the problem but also simulates the way flesh and skin shrinks when moving, this also fits the stretch and pull principle of animation and is often used by professional animation artist.

If it is the parenting problem then it could also solve it self when you you are done with the rig.

If this doesn't solve your problem then it could be a vector translation problem or overzealous animation interpolation.

More information would be needed for solving these, if you provide us with the engine you are using and the exporter it would be easier to solve and understand.

First: In your previous post describing what sounds like the same problem, you surmised it was a problem with your "converter" and not with the animation end of things. Have you determined you're importing the same data the max is using?

Second: [Wild Guess follows -->] Unfortunately, if the problem is the use of splines, I can't guide you through the math (I'm pretty weak in that area myself). However, if the problem only occurs when you use IK, then I suspect max uses a spline solver for both the data and the display. So this is just a guess on my part - I'm thinking you'll have to consider the keyframes as spline control points, rather than interpolation end points. Hopefully others here can provide more guidance.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

My best guess normally would be you need to add more to it's resolution.

But, I am also guessing that you checked to make sure that it's keyframe animation for the heel remained in place? If not, you'd might want to give that a look. I learned that a good amount of people forget that IK is usually blended into a FK rig, so moving something a hair out of normal range will cause something else to shift so the bone does not stretch.

If that was the case, then make a plane for the feet to snap to when you are animating.

Hi guys,

Thank for helping. I think I should provide more information. This is kind of long so please bear with me.

I am creating my animations in 3dx Max; for this situation I set two keyframes (those pictured), one at frame 0 and the other at frame 5 (the frame numbers are arbritray as you'll see.) I then export via FBX using the 'bake animation' option, if you're not familiar with that it simply resolves or 'flattens' everything and outputs the bone data for every frame, so I end up with keyframe (sample) data at frames 0, 1, 2, 3, 4, 5.

My FBX loader extracts the data for keys 0 and 5 (the two I specifically set) and I use those for my two poses (animation samples.)

Here is the totality of what I extract from the FBX file:

1) bone offsets (the 'bind pose' or 'T pose')

2) the keyframe poses (two in my case, at frame 0 and 5)

3) mesh and vertex weights (not relevant here)

I convert then store that data like this:

// for the offsets

struct SkeletonBone {
 float4x4 offset;
};

// I only need a single SkeletonBone array since that never changes

struct Skeleton {
 SkeletonBone* bones;
 int num_bones; // three in this scenario
};

// for the keyframes (poses or samples depending on terminology)
// it's the basic SRT without the scale

struct Bone {
 float3 lclT; // local translation
 float4 lclR; // local rotation as quaternion
};

// each array of the above Bone struct is an animation sample (I call them poses)

struct Pose {
 Bone* bones;
 int num_bones; // three in this scenario
};

// for this scenario I would have an array of two Poses

Pose poses[2];

If you like I can provide the routines I use to convert the raw data to the offset matrices and SRTs but it's nothing fancy.

I then have everything I need to generate the matrix palette every frame. For testing I run a 30-iteration loop, increment the interpolation value t from 0.0 to 1.0, and transform the ankle bone position by palette[2]. I can include that code as well but it's standard stuff.

I really don't think my math is wrong. I've created bone chains with 12 bones, did crazy xyz rotations on all 12 bones, transformed the last bones' positions with the matrix palette and they're perfect, exactly where the bones are in 3ds Max.

All I can think of is that I need more animation samples for an animation like this, it will definitely reduce the increasing error as t approaches 0.5 but i still feel like something is wrong.

Also, this is not IK releated at all, I was mistaken; IK doesn't even enter the picture since I'm baking the animations.

Here is my Nlerp function:

 
// I like it better like this
typedef DirectX::XMFLOAT4 float4;
 
void quaternionNlerp(float4* out, float4* q1, float4* q2, float t)
{
 float dot = q1->x * q2->x + q1->y * q2->y + q1->z * q2->z + q1->w * q2->w;
 
 // I read to check the dot but I see no difference either way
 
 if (dot < 0.0f){
  // negate one of the quaternions (q2)
  out->x = q1->x + t * (-q2->x - q1->x);
  out->y = q1->y + t * (-q2->y - q1->y);
  out->z = q1->z + t * (-q2->z - q1->z);
  out->w = q1->w + t * (-q2->w - q1->w);
 }
 else{
  out->x = q1->x + t * (q2->x - q1->x);
  out->y = q1->y + t * (q2->y - q1->y);
  out->z = q1->z + t * (q2->z - q1->z);
  out->w = q1->w + t * (q2->w - q1->w);
 }
 
 // do the normaliztion with DirectX math
 
 DirectX::XMVECTOR xmv1 = DirectX::XMLoadFloat4(out);
 DirectX::XMVECTOR xmv2 = DirectX::XMVector4Normalize(xmv1);
 DirectX::XMStoreFloat4(out, xmv2);
}
 

Maybe I can give you the actual data and you can run it through your animation system ? It's only three bones and two poses, not too much.

That's about all I can think of. Thanks again.

Do you apply any further logic of translating the root/center/pelvis node, or do you view the object space frames interpolated, out on the pictures?

I would expect if the rotations weren't quite right, the heel would also deviate in the x axis a bit - but if none of the children receive any translations - it is weird that it would only move in the y direction -- unless somehow the rotation deviations were somehow perfectly altering to keep the heel on the x-axis -- in which case I'd be wondering about how it is baking the output data - maybe even wondering about animation-easing having some involvement. (?)

Errors propagate down bone hierarchies very easily. It is enough of a problem that at Square Enix that we have had to go very far out of our way to minimize it.

#1: You haven’t applied key-frame reduction/addition.
Your run-time should be making only linear interpolations, so in order to keep a reliable reproduction of an animation with complex interpolation modifiers etc. you must add fake key-frames at a given small fixed interval (once every 15 milliseconds or so, up to you) and then use the Autodesk® FBX® SDK to sample the data at that time.
If the sampled data is close enough to your generated data then you can eliminate the key-frame.
For example, if you have key-frames A, B, and C, use A and C to interpolate your own version of the B key-frame while using the Autodesk® FBX® SDK to generate a reference B. If they are close enough, you can eliminate B as a key-frame.

This will likely eliminate a large amount of accumulated errors you are getting, forcing your exporter to check where it is inaccurate and add key-frames there to correct for it.


#2: Your use of quaternions is surely introducing a lot of accumulated error.
The data was authored via an X, Y, and Z rotation, each value interpolated independently of each other. If you want a faithful reproduction of the animation you should stop combining them into quaternions. Not only will this improve your accuracy but it will give you much more freedom later when you want to do more than just play animations (when you want to start getting dynamic about where your character’s head points, etc.)


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Hi L. Spiro,

That's reassuring.

I pretty much understand #1 but I have to work it out in code.

I don't really understand #2 though. Do I not use quaternions to store the rotation part of the bone SRTs, and store the Euler angles instead ? Then, when creating the matrix palette, do I simply lerp the rotations individually and create a rotation matrix from those values ?

Thanks again.

I don't really understand #2 though. Do I not use quaternions to store the rotation part of the bone SRTs, and store the Euler angles instead ? Then, when creating the matrix palette, do I simply lerp the rotations individually and create a rotation matrix from those values ?

That is how the authoring software works, so if you want the most accurate reproduction of the animation this is how you have to do it.

The problem with merging rotations into a single value (be it a matrix or a quaternion) is that eventually when you get around to making scene editors one of the features you will want to add is the ability to animate a single component of rotation from the editor (make it rotate at a fixed speed or follow a curve or follow another object). If you don’t actually have the original X, Y, and Z rotation components then you will have to hack them in, which is already bad enough.
If it is playing another animation at the same time there will be a conflict between the forward-kinematics animation and your procedural animation and it is impossible to resolve it such that every case is handled properly.

The difference in performance isn’t notable, for the sake of stability and correctness it is the best way to go.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement