go to Tutorials Page | go
to 3DKingdoms.com

by Jonathan Kreuzer

Other Tutorials I've written:

Normal Mapping with Skeletal Animation | 3D Selection | Tips on programming a 3D Editor

You might also want to read my Weekly Articles related to this topic:

Skeletal Animation #1 | Skeletal Animation #3 |
Feet to Terrain | Quaternions

I'll add more later when I have time (more illustrations, explanation, advanced stuff and code, easier to follow, etc)

**Contents - Inverse Kinematics Tutorial **

*A humanoid skeletal structure, where each hand and foot is attached to
a certain location on a propbox. The centerpoint of the skeleton can be moved/rotated,
or the propboxes can be moved/rotated, and the skeleton will try to maintain
the attachments.*

**About Inverse Kinematics for Humanoid Skeletons:**

Okay, the first question I'll answer is, just what exactly is Inverse Kinematics? When I told my friends (many are programmers, though not necessarily interested in the same types of programming I am) I was working on Inverse Kinematics, most asked what that meant. If you were doing Kinematics you would be calculating the position of an object from its rotations/velocities/forces on it. Inverse Kinematics is the opposite. You're given the position and need to calculate how to reach it. In the case of this tutorial Inverse Kinematics means calculating the rotations of the joints in the skeleton for a certain part of the skeleton to reach a given point.

So now the next question is, what would this be useful for? I'll just give a few examples, there are plenty. For instance, let's say you want a character to hold a staff with both hands, and swing it around. If you had a sword held in one hand you could just calculate its position and rotation for each frame from the position/rotation of the hand holding it, and that would work fine. But if a character is holding a staff with two hands, that won't work. Even if you went through the trouble of moving the second hand onto the staff for each keyframe, when interpolating between the frames the other hand would come off the staff. Using IK, you can give each hand a position on the staff, and then translate or rotate the staff and they will follow it. Another example would be to use IK to adjust animations in realtime, for example the user throws a ball, and the computer moves its player's glove to catch it. Or a character is dropped onto random uneven terrain, and keeps his feet at different heights to stand or walk on the terrain. Or a character is walking down stairs, and automatically moves his hand to hold onto the railing, and moves his feet to adjust to different sized stairs.

Now I don't want to give the impression that it's easy to create good animations with IK or use realtime IK and have everything look okay and work how the user would expect it. Adjusting animations to the game world in realtime is tougher than adding IK as another tool for an animator to use when creating his animations. The reason for this is that the animator is on your side, he/she wants same thing that you want, to create good animations. That means an animator will purposefully avoid situations where your IK gives a poor result (uncomfortable or impossible positions, limbs moving through other parts of the body, etc,) or adjust the result to make it better. A game user might even do the exact opposite =) So an IK system would have to be more robust for realtime uses than for animation creation. An IK system that always gives the results you want is impossible, but I do have more work I want to do on mine. (See bottom paragraph for a little bit more on this.) I hope to work on getting good realtime IK results for some specific situations I have in mind soon.

First off, I don't think I've ever looked up anything about IK, I just made up this system myself using what I know about mathematics. Now, while it's possible this could give me "fresh perspective," it more likely means if I had done some research I would have either found similar or better ideas. This system is not a general purpose system, it's tailored specifically for humanoids (humans and creatures with the similar joint&bone structure to humans.) Since this is the most common type of character by far, I figure it should be quite useful. While for a more general purpose system I might try an iterative approach, this system just flat out calculates the rotations needed. What follows is a description of some of the techniques I use to do IK in my animation editor in 3D Kingdoms Creator. The tutorial starts simple and builds up to a more complicated and full-featured system. Since there are lots of things that can go wrong, it's good to test parts separately and make sure they work.

( *A diagram of
my humanoid skeleton, with the labels for what I'll name the joints in case
there's any confusion. I have a 'Shoulder' joint and a joint that controls
shoulder position. For this skeleton all 4 fingers on a hand are treated as one,
although I can separate them if I need to. This diagram is also missing the
thumb joint at the base of the wrist.*)

Let's say I want my character's hand to reach a certain 3d target point. You should have the (x,y,z) locations of all the joints in your skeleton for the current position stored. So I take the location of the 'shoulder' joint, and find the distance from here to the point I want to reach. Pretty straightforward so far I hope.

The bend of the elbow joint controls the distance of the hand from the shoulder.
The elbow is a hinge joint meaning it only bends along one axis, so finding
the angle the elbow joint bends to reach a certain distance is a 2-dimensional
problem. The problem can be viewed as finding angles in a triangle, with the
distance to the target viewed as one side of a triangle, and the length of the
upper arm and forearm bones as the other two sides. Now if you can remember
all the way back to high-school Trig, you'll remember that you can compute any
of the angles in a triangle if you know the lengths of all 3 sides. Here's a
line of C code with the formula for computing the angle (a1) opposite of s1
(side1).

float fDegrees = (float) acos ( (s2 * s2 + s3 * s3 - s1 * s1) / (2 * s2 * s3)
) * ( 180.0f / 3.1415f );

If you plug in the distance between the target and shoulder as s1, you'll have
the angle of the bend of the elbow to make the arm reach that distance.

You should note that there are 2 possible results for an elbow bend that could satisfy the arm reaching the right distance. For instance, if 45 degrees satisfies this, then -45 degrees would too. So here would be a good time to introduce some constraints on the bending of the elbow. We can just store a minimum angle and a maximum angle. Let's say 0 degrees for straight, and 133 degrees for the maximum bend. Since I'm using 0 degrees for straight, and the formula will return 180 degrees for straight, I'll set the ElbowBend = 180 - fDegrees. Now either only one of ElbowBend and -ElbowBend will fall within this range, in which case we'll use the one that does, or neither will fall in this range. If neither are in the range, the arm is unable to reach the point (at least without moving the shoulder position.) To reach as close as possible, choose the value of ElbowBend that comes the closest to the bending range then clamp it to the range. A possiblity that we can should check for before finding the angle is whether the distance to the point is longer than the upperarm + forearm lengths, meaning again the arm can't reach the point, so we'll use a 0 degree ElbowBend to get as close as possible.

So now that you have the ElbowBend, you need to find what rotation of the shoulder. This is a 3D problem now, so I'll calculate a quaternion for the shoulder. We'll start by aiming the shoulder directly at the point by calling the QuatAim function with the shoulder point and the target point (See QuatAim function.) Now the first thing to consider is that this gives the worldspace rotation to reach the point, but the rotation of the shoulder joint needed will be different because in skeletal animation rotations are accumulated from previous joints, so the shoulder already has a certain worldspace rotation. I have the world space rotation at each joint stored as a quat, so I invert this quat and multiply it with the one returned by QuatAim (I'll call the quaternion result ShoulderQuat). So now the upper arm is pointing directly at the target point, but the upper arm should bend to compensate for the elbow rotation. To find ShoulderBend, use the triangle formula again, this time with the forearm length plugged in for S1. Note that if you negated ElbowBend, you'll have to negate this one too. Now we create a quaternion that rotates ShoulderBend degrees around the same axis as the elbow rotates around, and multiply it with ShoulderQuat. Now the base of the hand will now touch the target point, or get as close to it as possible. Reaching a target point with the character's leg and foot can be done the same way. For grabbing on to moving objects, the target point can be defined as (x,y,z) coordinates relative to an object, then the world space coordinates of the target point can be calculated using the position/rotation of the object.

-- Insert Illustration?--

One problem is that the shoulder can be oriented in an infinite number of ways
and still actually touch the point. All the other solutions are merely twisting
a different number of degrees about the axis created by a vector from the shoulder
point to the target point. So a question is if the twist of the rotation that's
been returned is reasonable. One thing you can do is set things up so that QuatAim
will usually return a reasonable result. There's an axis that the QuatAim function
given will rotate around without twisting, and before calling QuatAim, the points
(shoulder and target point) are rotated into the proper space to make the arm
rotate forwards and backwards without twisting. (--Add More Here about setting
it up so that the arm doesn't twist funny, etc.-- Also you could choose a twist
to try keep the wrist rotation and/or shoulder rotation, reasonable --). If
the IK is for an animator program, you should also allow an animator to adjust
the twist of the arm himself to whatever he prefers for the animation.

Adding shoulder adjustments to IK for arm movements is a good idea. For instance, if you move your arm to reach across your body, your shoulder moves forwards. If it didn't your arm would have to go through your body. One way to check for this is to just use a plane along the side of the body the shoulder is on, and use the dot product to check which side of the plane the target point is on and how far, and depending on how far it crosses the body, rotate the shoulder position forward (assuming the target point is forwards of the body plane.) Also when the arm is raised above the top of the body plane the shoulder position will start to rotate up. -- Write More Here --

One common thing that you will want is keeping the hands and feet at the same orientation relative to an item, like a box or staff
being held, or the ground for feet. When I grab a hand to something, I store a quat with the worldspace rotation the hand (including the rotation of
the hand joint). Then quatmultiply the stored orientation with the inverse of the current worldspace rotation (not including the hand joint.) You can
then quatmultiply with a quaternion containing any change in rotation of the object the hand is attached to.

For feet you will want to add toe bends too, because an important part of walking or crouching in certain positions, or even standing on
very uneven terrain, is having the heel lift off the ground, and leaving the front part of the foot on the ground. I have a limit angle set for how
far the foot can bend toward the leg, and if the angle requested for the foot brings the angle between the foot and leg past my limit, I clamp to the
limit and lift the heel off the ground, bending the toes to stay even with the ground.
Also standing on the toes of the feet will allow the leg+foot to reach further,
so can also lift the heel off the ground if the target is beyond reach
otherwise.

-- Add paragraph about adjusting hands and feet to lie on a plane or terrain. Also could use similar strategies for more collision resolution.--

You need to consider the order of the calculations, since joint changes further up the skeleton hierarchy effect their children. So for instance to move the arm to a certain point in one pass, you would adjust the shoulder position joint, then the shoulder and elbow joint, then the wrist joint. A single pass shouldn't take too long though, so for various reasons ( resolving collisions, adding extra movements further up but only for certain situations, etc..) you might want to do more than one pass. If implemented correctly the skeleton should reach the target position exactly and stay locked exactly onto the target when this is possible. I don't mean it's completely exact, I use floats for the calculations, and if for instance I just calculate the foot position on the terrain based on its previous position, there is a very slight drift from accumulating tiny errors, so I always store a target point.

-- Add More Here --

What to do when the impossible is requested is a question that will have different answers for different situations. If the target point is out of reach of, the skeleton could reach as close as possible, it could adjust joints further up the skeleton hierarchy to help reach the point, or the position of the skeleton itself, or it could stop the target from moving out of reach in the first place, and there are probably a few other possibilities. Or reaching the target point could involve moving the hand through other parts of the skeleton or nearby objects. Much of this you could ignore in an animation program and rely on the animator to resolve difficulties. For realtime calculation in a game/program you would need to be check for and resolve these issues or limit the situations where the IK is used. The more freedom allowed where IK is involved, the more annoying it will be to try to get everything always look right.

**Various Functions (Not Finished):**

// quat (qr) aims z-axis along vector from point P1 to P2 void QuatAim (quat &qr, CVec3 P1, CVec3 P2 ) { CVec3 V1 = P2 - P1; Normalize(V1); qr.x = V1.y; qr.y = -V1.x; qr.z = 0; qr.w = 1 - V1.z; if (QuatNormalize(qr) == -1) { quat Qtemp = { 0, 1, 0, 0 }; qr = Qtemp; } } |