Lesson 8 : Advanced foot rig
In this course we will see how to make a rig system for feet. Which will allow us to have foot roll, banking and all
Let's look at a simple and effective system that allows you to manipulate the foot in all directions and in all possible ways, a system designed to make the foot as natural as possible for animators.
Table of Contents
1. How to place bones
2. Banking / edit pivot / twist feet
3. How to make a foot roll system
4. How to connect feet to legs
5. Skin feet
6. Sources
In this course, we will explore the fundamental principle that will allow us to perform all foot movements, enabling us to create a foot that can move in all directions, create foot halls, and spin the foot in every possible direction.
We will need to use a system similar to the one we created for the hand. We will reuse techniques such as set driven keys and remap values on the joints. This will allow us to create rotations on each part of the rig, enabling us to perform various movements.
The primary goal will be to create multiple groups that will overlap, allowing us to connect them with remap values and driven keys to execute movements. All these groups will be directly linked to the foot, allowing us to move the foot in any desired direction.
1. How to place bones
To complete this step, there will be two hierarchies: one for the bones and another for the groups. The bone hierarchy will be used for skinning the meshes, which means attaching them to our foot. This will allow us to deform our foot in any desired situation. The advantage is that there won't be many bones, except if we want to rig the toes or not.
As for the group hierarchy, it will be used for positioning and all the different connections for the driven keys and remap values. These groups will enable us to perform all the desired movements, and they will drive the bone hierarchy to execute the movements directly on the foot.
​
Here are images representing the different hierarchies we want to achieve.
We will start with the bone hierarchy. Placing it will be quick and efficient. In our situation, we will not rig the toes because it involves repeating exactly the same process as in the course for rigging the hand. You can use the same techniques for the toes. In our case, we will use a single bone that will allow us to bend all the toes at once, but you can certainly create a system where each toe is independently controlled.
​
We started by placing a first joint, "jnt_foot_ankle_L", at the ankle. If you are doing this lesson after having already rigged the other body parts, placing this bone correctly is very simple. You just need to match the transform of your joint so that it automatically positions itself in the correct location.
Once this is done, you need to place a second joint, "jnt_foot_toes_L," at the joint of your toes. It should be aligned with your foot so that the controllers can be positioned correctly later.
​
You must, of course, follow the rule of 2/3 to place your joint precisely in the right spot, ensuring that deformations occur coherently.
​​
When placing this joint, it is important to parent it to "jnt_foot_ankle_L" and then move it to position the "jnt_foot_toes_L" joint. We do this to ensure that there is only one translation in the rotation data of the "jnt_foot_toes_L" joint. Remember, the fewer rotation, translation, or other data points you have, the more accurate your deformations will be, reducing the risk of bugs and issues that can compromise your rig.
For the next step, you have two options: if there is a shoe or something covering your foot, you can place a joint at the tip of your toe. This joint will help with the initial skinning process but won't be used much afterwar, it will serve more as a visual aid and save you time.
​
If you are in a situation where the toes are exposed, you can place joints for each toe in the same manner as in the hand rigging course.
​
In our case, we won't dwell on this because it would involve repeating the same exercise as for the hand. Therefore, we will proceed with the first solution.
Once you have done this, we will need to tackle the group hierarchy. To create this hierarchy, we will place groups at strategic points on your foot, allowing us to capture all the possible movements your foot can make.
​
Before starting, let's explain their purpose. There will be four groups located at the contact points of your foot with the ground, which will handle all the movements such as banking forward, backward, to the right, and to the left.
For ease of visualization, we have replaced groups with joints in this example.
Next, we will have another set of groups, which will handle everything related to the ball of the foot. These groups will help us achieve realistic walking movements for our foot, allowing us to decompose our animation. These groups will primarily be used to move the joints of our foot.
For ease of visualization, we have replaced groups with joints in this example.
Next, there will be a group located on the ground, positioned between the front and back foot groups. This group will be used to spin the foot.
It can be moved between the front and back of the foot to create a system where the rotation center of the foot is editable. This setup allows for the rotation to always be performed by the same group, but by moving this group along a line, we can shift the pivot center of our foot.
For ease of visualization, we have replaced groups with joints in this example.
To start placing the joints, we will create three groups that we will parent in a parent-child hierarchy, naming them as follows: "grp_foot_banking_heel_L" -> "grp_foot_spin_L" -> "grp_foot_banking_toes_L".
​
The first group should be placed at the heel of the foot, and the last group should be placed at the tip of the toes. It is important that all three groups are perfectly aligned and that there is only one translation in the groups. Additionally, ensure that the main rotation axis is the X-axis to avoid gimbal lock issues.
​
The exact placement of "grp_foot_spin_L" is not critical at this stage, as its precise location will be adjusted with notes later on.
To better visualize the placement of the different groups, we will add a locator to each group called "tmp_visualizer." These locators will be removed later once they are no longer needed.
Once these elements are in place, we will add two more groups: "grp_foot_banking_outside_L" and "grp_foot_banking_inside_L". These will allow us to perform lateral banking on the sides of the foot. To do this, you need to make precise adjustments to find the exact contact points on the ground. Since the foot is not straight and proper banking requires perfect joint placement, accuracy is crucial.
​
To achieve this, you will need to capture the outer convex hull line on each side of your foot. For this, you'll need three locators: the first locator will aim at the third one, and the second locator will be parented to the first. This setup will allow you to move the second locator along a line. Place the locators at the vertices farthest from your foot to capture the outer convex hull line. If you are not familiar with a convex hull, here is a link that explains what it is click here.
​
After placing your locators, position the second locator approximately in the middle, and match the transform of your group to this locator. This will give you the exact rotation and placement needed so that your rotation center is positioned correctly.
Once this step is completed, you will only need to parent the groups into one another to form a single parent-child hierarchy.
The final step will be to place the last three groups, which are positioned above the ground. This process will be relatively quick and efficient. Two groups will have their transforms matched to the joints we created for the foot, and the last group will be positioned based on the height of the "jnt_foot_toes_L" joint and the tip of the foot.
First, we will create the three groups, naming and parenting them as follows: "grp_foot_end_toes_L" ->
"grp_foot_toes_L" -> "grp_foot_ankle_L". The first task will be to position the "grp_foot_end_toes_L" group.
To position it, you should first parent this group to "grp_foot_banking_toes_L" and set its transforms to zero. This will ensure a single parent-child hierarchy. Finally, apply a constraint to the group using "jnt_foot_toes_L" as the reference, with the Y-axis as the constraint axis only. This will make the group align precisely with the joint.
​
For the other two groups, match their transforms to the corresponding joints, "grp_foot_toes_L" should match transform to "jnt_foot_toes_L", and "grp_foot_ankle_L" should match transform to "jnt_foot_ankle_L".
If you have correctly placed the different groups, be careful not to orient them in any random direction. You should achieve a result where all the rotations on the X-axis are zero. This is crucial for the subsequent steps to ensure that deformations and rotations function correctly.
2. Banking / edit pivot / twist feet
In this step, we will make all the necessary connections to start creating the system that will allow the feet to move correctly. To accomplish this, we will mainly use remap values. We will simply connect attributes to the remap values so that we can later control the movement of the different groups on the X rotations.
But before we start, we need to create a controller with various attributes that will allow us to manage the different movements we want to perform. We will create the following attributes:
Banking In Out: min = -10, max = 10
Banking Heel Toes: min = -10, max = 10
Edit Spin: min = 0, max = 10
Spin: no max, no min
After creating our controller and the various attributes, we can begin creating our first remap values. We will start with managing the banking inside and outside, which involves the right-to-left banking of the foot. To do this, we will need two remap values.
The first remap value will handle the negative values of the "Banking In Out" attribute. Set the input min to 0 and the input max to -10. Connect the output of this remap value to the X rotation of the "grp_foot_banking_outside_L" group.
Next, take a second remap value to handle the positive values of the "Banking In Out" attribute. Set the input min to 0 and the input max to 10. Connect the output of this remap value to the X rotation of the "grp_foot_banking_inside_L" group.
After completing this step, set the output max to 90 for both remap values.
If any of the rotations occur in the wrong direction (for example, if the groups move up instead of down or vice versa), simply replace 90 with -90 in the remap value that is causing the incorrect rotation.
If you have understood the method described above, it will be straightforward to apply the same approach to the "Banking Heel Toes" attribute. You need to create two remap values: one for negative values with input min = 0 and input max = -10, and another for positive values with input min = 0 and input max = 10. Connect the remap values to the appropriate groups and link them to the corresponding attribute. Finally, set the output max to 90 for both remap values.
Check if the rotation behavior is correct. If not, simply replace 90 with -90 in the remap value causing the incorrect rotation.
This step will be a bit more complex as we need to create a system to move the "grp_foot_spin_L" group to adjust its position between the "grp_foot_banking_heel_L" and "grp_foot_banking_toes_L" groups. We cannot simply move the group along the Z translation axis, as this would shift the entire hierarchy after the Spin group, which is not desirable.
To move the Spin group without affecting its child groups, we need to create a system that counteracts the movement of the "grp_foot_spin_L" group. Essentially, we need to reverse these movements. If the Spin group moves forward by 2 units, the "grp_foot_banking_toes_L" group should move backward by 2 units.
To achieve this, we'll need to do a bit of math. First, you need to determine the maximum distance between the "grp_foot_banking_heel_L" and "grp_foot_banking_toes_L" groups. To find this distance, simply add the Z translation values of the "grp_foot_spin_L" group and the "grp_foot_banking_toes_L" group:
In our case: 9.314 + 13.104 = 22.418.
​
​
Now that we have this distance, the calculations will be straightforward. We will simply perform a multiplication on this value to determine the distance of the "grp_foot_spin_L" group. Knowing the maximum distance the group can travel means that multiplying this value by 0.5 will place the group at the midpoint of the distance.
Thus, using the attribute we created, we can multiply the maximum value we calculated earlier by this attribute to manage the distance of the "grp_foot_spin_L" group.
(Just remember to use a multiply divide node to divide the attribute value by 10 before multiplying it by the maximum value, so that it is within a range of 0 to 1.)
Next, for the "grp_foot_banking_toes_L" group, to determine its position, we want to counteract the movement. This means performing the opposite of the movement of the "grp_foot_spin_L" group. To do this, simply subtract the distance we calculated (multiplied by the attribute) from the maximum value. This will give us the opposite position.
For the last attribute, "Spin" it will be extremely simple. You just need to connect the "spin" attribute directly to the Y rotation of the "grp_foot_spin_L" group. Once this is done, all the attributes will function correctly if you have completed the previous steps properly.
3. How to make a foot roll system
For the various movements of your foot, there is one last step called the foot roll. This element is extremely useful in animation because, with each step we take in real life, we perform this movement. The foot roll involves lifting the heel off the ground while keeping the toes in contact with the ground. After the toes lift off the ground, the foot returns to a flat shape.
The first thing to do is to create an attribute called "Foot Roll" which will allow us to play the animation of our foot performing the desired movement.
To achieve this kind of movement, you'll need to create a system that first applies rotation to the group "grp_foot_toes_L" and then continues the rotation on its parent group, "grp_foot_end_toes_L", finishing at 90 degrees. However, as soon as the second group starts to move, you'll need to ensure that the rotation of the first group, "grp_foot_toes_L", returns to zero.
To accomplish this, you will need to use a remapValue node. The first remapValue will drive the group "grp_foot_toes_L", with input values ranging from 0 (input min) to 10 (input max), connected to the "Foot Roll" attribute. The second remapValue will drive the group "grp_foot_end_toes_L", with input values ranging from 5 (input min) to 10 (input max), also connected to the "Foot Roll" attribute, with an output max of 90 degrees.
Here are some reference videos you can analyze to understand what a foot roll looks like. Observe the deformations and movements of the foot to see how it behaves during this action.
Once that's done, you'll need to connect the outputs of the remapValue nodes to the appropriate channels. For the remapValue node with input values ranging from 0 to 10, connect it to the X rotation of the "grp_foot_toes_L" group. For the second remapValue node, connect it to the X rotation of the "grp_foot_banking_toes_L" group.​
​
However, there's a small issue: there is already something connected to the X rotation of "grp_foot_banking_toes_L." To resolve this, you should use an addDoubleLinear node to combine the two rotation values.
There will be just one last step to create your control: setting up the remapValue node that ranges from 0 to 10. First, you need to adjust the curve of the remapValue to make it non-linear. By default, when we create a remapValue node, the curve for the values is linear, as shown below.
In our case, since we want to create a system where the first part of the foot starts to bend and then returns to a flat position, we need to set the value so that it activates and then deactivates accordingly.
In this situation, we want the first part of the foot to rise when the value is between 0 and 5, because this is the exact time needed before the toes begin to lift off the ground. Therefore, the maximum value should be reached halfway through this range. This means that when the input value is 5, we should be at the maximum output value.
Since we want the rotation to reset after the input value exceeds 5, you need to ensure that it returns to a value of zero. To achieve this, you should modify the curve in the remapValue node:
-
Add a Key Point: Insert a key on the curve at the input value of 5.
-
Set Output Value: At this key point, set the output value to the maximum rotation value you want.
-
Create a Return to Zero: Add another key point beyond the input value of 5, and set the output value to zero.
​
This adjustment ensures that after the input value surpasses 5, the output value will drop back to zero, making the rotation return to its default position.
Next, you need to set the maximum value in the curve to approximately 30. This value will determine the inclination of the first part of the foot before the toes start to move.
​
At this point, your control will be functional. You will just need to test and adjust to find the default maximum value that suits your needs. We’ve used 30 as an example, but you can set any value you prefer. Additionally, you might want to create attributes for the output max values of the two remapValue nodes. This will allow you to fine-tune how the foot behaves, including the maximum inclination before the toes lift off the ground and the maximum inclination of the control when the input value is at 10.
4. How to connect feet to legs
I understand that things might seem a bit abstract at this stage, since we've been animating groups and haven't yet connected them to the joints, making the purpose less clear. In this step, we'll connect everything, which will make much more sense. By linking the groups to the joints, we’ll be able to move our foot in both IK and FK modes.
The advantage of this system is its versatility. Using pairBlends, we can seamlessly switch between IK and FK modes.
Here's how to set it up:
1.Create a PairBlend for "jnt_foot_ankle_L":
-
Use only the rotation in this pairBlend, as the joint's position will be handled by a point constraint.
-
Set the pairBlend to reference the group "grp_foot_ankle_L" for IK mode and "c_Ankle_L_FK" for FK mode.
-
Don't forget to use decomposeMatrix nodes to retrieve the rotation from the world matrices.
2.Create a PairBlend for "jnt_foot_toes_L":
-
Follow the same approach as with the ankle joint. Use the group "grp_foot_toes_L" for IK mode and the FK control for FK mode.
-
Again, utilize decomposeMatrix nodes to get the world matrix rotations.
3.Connect the IK/FK Switch Weight:
-
Link the weight attribute of your IK/FK switch (for the right leg) to the pairBlends.
​
This setup will enable smooth transitions between IK and FK modes for the foot, ensuring that both the ankle and toes joints behave correctly in both modes.
Indeed, switching between IK and FK modes should ideally not result in any undesired rotation if all controllers are at zero and perfectly aligned. If you notice unwanted rotation during the switch, it typically means that the reference controllers do not have identical positions, which leads to discrepancies when transitioning between modes.
To address this issue, follow these steps:
1.Create an Offset Group:
-
After the FK control ("c_Ankle_L_FK"), create a new group called "offset_Ankle_L_FK."
-
This offset group will help align the FK control with the IK joint.
2.Match Transforms:
-
Align the "offset_Ankle_L_FK" group with the "jnt_foot_ankle_L" joint.
-
Ensure that this group has the same position, rotation, and scale as the joint, so the orientations match perfectly.
3.Update PairBlend References:
-
In your pairBlend node for "jnt_foot_ankle_L," use the "offset_Ankle_L_FK" group for FK mode instead of the original FK control.
​
By doing this, you ensure that the FK control and the joint have identical rotations, thus eliminating the unwanted rotation when switching between IK and FK modes. The offset group essentially corrects any discrepancies, providing a seamless transition and perfect interpolation between the modes.
Now we can switch the rotation between IK mode and FK mode. As for translation, it will be simple; we just need to use a point constraint referencing the "cstr_twist_ankle_L".
With this technique, we will be able to easily switch from FK mode to IK mode. The foot will always follow the leg.
Once we have set up the point constraint, the foot will be able to move and orient itself correctly. Now we need to ensure that when we manipulate the "c_Ankle_L_IK" controller, all the groups in the hierarchy created for banking can follow the global movements of this controller.
To do this, we will create a group called "hook_foot_L", where we will connect the World matrix of the controller to its Offset Parent matrix. We must remember to disable the inherits transform option for the "hook" group.
Next, we will create a group called "offset_foot_L" which will allow us to maintain the initial position of the "grp_foot_banking_outside_L" group. Finally, we just need to set up the hierarchy as follows:
"hook_foot_L" -> "offset_foot_L" -> "grp_foot_banking_outside_L".
Once we have done this, we will encounter a problem. When we activate the various banking controls, the foot rotates correctly. However, the leg and the foot remain static, whereas in our case, we want the leg and the foot to follow the position of the last group we created, "grp_foot_ankle_L." In real life, when we lift our foot off the ground, the leg follows.
To remedy this, it will be very simple. You just need to modify the placement of your "ikHandle" within your IK hierarchy. Indeed, it is thanks to this node and its position that the IK system works, so you just need to parent this node to the "grp_foot_ankle_L" group, and it will work.
There will be one last step to ensure the foot works perfectly. Our foot functions as we want, but we cannot move the toes at the moment, and if we activate our foot roll, it will not work. We need to add a controller in the FK hierarchy to move the toes and then use a pairBlend to switch from IK mode to FK mode.
To achieve this, we will create a controller that we will directly match-transform with the "jnt_foot_toes_L" joint. Next, we will create a pairBlend node that will serve for the IK/FK switch of the toes. We will connect the rotation output of the pairBlend to the "jnt_foot_toes_L" joint.
Then, we will use the remapValue node that we used to rotate the "grp_foot_toes_L" group to invert it and use it as the rotation input for the pairBlend in IK mode. For the rotation in FK mode, we will simply use the controller we created.
If everything works and is in order, you will then need to parent the "root_controller_foot_L" group to the "hook_foot_L" group so that the "root_controller_foot_L" can follow the movements of the foot.
5. Skin feet
The last step, if needed, is to skin our joints to the foot. This will be very simple and quick. You just need to take the three joints we created for the foot, add them to the current skin of your character, apply a smooth skin, and it will be done.
skin before smooth
skin after smooth
Thanks to this lesson, we have seen how to create a foot that is easy and ergonomic to use, even if it is a bit complicated to set up. The advantage of this method is that it uses the same principles as the hand rigging lesson, which should be much simpler to understand and replicate. You can always make the foot more complex by adding toes and creating animation controls with a controller, as we did with the hand. Once you understand the technique, your only limit is your imagination.
I hope this lesson has served you well !