I have been thinking about split joints an unhealthy amount of time and finally found a solution that feels satisfying. I will discuss the pros & cons of some alternatives first, but feel free to skip to my approach or the downloads directly.
- The problem
- Solution: Split middle joint
- Split options
- Option A: Matching orientation
- Option B: Matching total length
- System inputs
- Main IK
- IK inception
- Two extremes
- Find the lower mid
- Find the upper mid
- Bifrost graph overview
Using two bones for organic limbs has an inherent flaw: The area around the middle joint collapses. Most noticeably at extreme angles, such as 180degrees: The two bones coincide and your geometry completely crashes into itself:
Let us first look at some options that are easy to set up. These help to preserve volume, but do not fix the problem of the bones & geometry collapsing onto themselves at extreme angles.
Using the Dual Quaternion or Weight Blended option of Maya’s skinCluster helps to prevent the corner geometry from collapsing (“candy wrapping”). This might be your simplest solution if your deformation is not too extreme.
Average middle joint(s)
Sometimes Dual Quaternion is not an option (games) or you might need more control. In that case, a common alternative to prevent volume loss, is an extra mid-joint whose rotation is constrained half-way between the first and second bone. You can add any number of such joints, offset them, or even add drivenKeys.
Solution: Split middle joint
To truly prevent the bones from collapsing onto each other, you have to split the middle joint. The total rotation is split between the two joints and because they are slightly offset from each other, your geometry won’t collapse into itself as much, even at extreme bends:
Splitting the middle joint of an IK chain is an old concept. But the question is: where do you place the split joints? I can think of two good candidates. Unfortunately they are mutually exclusive:
A: Matching orientation
PRO: The orientation of the first and last bone matches the original chain.
CON: The total length of the split chain is different from the original chain. This can be alleviated by gradually scaling the joints, as the chain approaches its maximum length.
B: Matching total length
PRO: The total length of the split bone chain matches the length of the original chain.
CON: The split bone orientations do not match the original chain. The original orientation can be maintained by adding a simple offset transform to the split joints.
Quick note why I bring up the total length of the chain so often: When layering multiple IKs on top of each other (one IK driving the next), it becomes quite crucial that they reach their full extension at the same distance (otherwise a child-chain might pop into a straight line while your main IK chain is still bent). For example in a limb that has an overall “Main IK” but needs a split in one of its joints:
This is doable with both options, but when you match the total length you see exactly what is going on and do not have to scale joints (and therefore geo that is skinned to them):
Option A: Matching orientation
This setup is quite easy: To maintain the original angles, your split joints must be somewhere along the axis of the original bones. Simply add two middle joints and slide these split joints along the original bones into a position you are happy with:
The joints of the resulting 3 bone chain can be scaled to match the total length of the original chain, as the IK approaches its full extension:
Feel free look through my scene, which uses this node setup:
Option B: Matching total length
Now onto the reason why I am writing this post. This inconspicuous alternative took me various attempts, spread over multiple years. I chased my flawed intuition twice! I was so sure that the split joints would lie on a circle, defined by the intersection of the perpendiculars in the middle of either extreme. This GIF is probably easier to understand than that last sentence:
As you can see at the top: It is a tiny bit off. “A tiny bit?!“, I hear you scream, and I agree:
Let us solve this issue properly!
I chose to expose three values to the user: Split Length, Split Bias & Rotation Bias:
- The Split Length is a positive float and simply the distance between the upper mid & lower mid.
- We want to maintain the total length. Therefore the Split Length must be subtracted from the two bones we start out with. I call the factor of how much we remove from the upper or lower bone the Split Bias (0.0 to 1.0):
- Split bone length = Split Length
- Upper bone length = original upper bone length – Split Length * (1- Split Bias)
- Lower bone length = original lower bone length – Split Length * Split Bias
- Lastly, we can rotate the split joints around the original mid joint, which I call the Rotation Bias (0.0 to 1.0).
- Start: The start position of the IK.
- Upper mid: The split position closer to the start.
- Mid: The middle position of the underlying two bone IK.
- Lower mid: The split position closer to the end.
- End: The end position of the IK.
- Pole vector: Defines the plane and direction of the IK (check this other post for more info about poleVectors).
- Original chain: The two bone chain we start out with.
- Split chain: The chain after splitting. The length is equal to the starting chain, even if it might not look like it.
The original chain is the foundation for any solution we pursue. Therefore our first goal is to find the mid joint. I implemented a simple IK solver in Bifrost, but for brevity’s sake I won’t go into any detail on that. The most special thing about it is that it uses reference (=”rest”) lengths of the two bones, instead of a reference position of the three joints. Defining the reference pose this way simplified some parts of the setup (we will use this IK solver multiple times).
Somewhat underwhelming, but we have our mid position. Yay!
If we find one of the split joints (I just happen to have chosen the lower), the other split joint becomes a trivial two bone IK problem:
For a given Split Length and Split Bias combination (both values are defined by the artist) we can find two extremes by creating a triangle where either the upper or lower split joint is the corner:
Both look a lot like a two bone IK as well, right?! We can find the Extreme A and Extreme B position by using our Bifrost IK solver with the appropriate reference lengths. This is where it comes in handy that the reference pose for our custom solver is defined by lengths, instead of positions.
Find the lower mid
We know that the lower mid position lies somewhere between its two extremes. An artist can use the Rotation Bias to dial in where exactly that is:
- Use the Rotation Bias to blend between the Extreme A and Extreme B position
- Get the vector from the end point to that blended position.
- Normalize the vector from step 2.
- Multiply the vector from step 3. by the lower bone length.
- We get the lower mid, defined by Split Length, Split Bias and Rotation Bias.
Find the upper mid
Now that we have the lower mid position, we get the upper mid position with another two bone IK between the start and lower mid position, using appropriate reference lengths.
And we are done. We can adjust the Split Length, Split Bias and Rotation Bias to dial in the exact split joint positions, without affecting the overall length of the chain:
Bifrost graph overview
- Calculate main IK to get mid position.
- Get the Extreme A & Extreme B position.
- Get the lower mid position.
- Get the upper mid position.
I only require initial joint positions from this setup. Therefore I did not make it a full-fledged IK solver:
- The resulting split IK chain is not very robust and does not support parenting.
- I did not run any tests, but the Bifrost graph is most definitely much slower than a C++ node.
- It might be better to define the split length as a factor of the original chain length, instead of as a length. The split would remain the same, in relation to the rest of the bones, when the rig is scaled.
Getting exactly what you see with this “match total length” option is nice, but it is by no means THE way. Often times a double joint is not even necessary (thin limbs and/or limited range of motion).
I hope this post gives you an idea of some of the different options to deal with limb deformation, so you can chose a suitable solution for your case. And if you ever need to match the limb length: Hopefully my approach can help you in some way.
Please share any question or insight to make this post better for the next visitor 🙂