top of page
  • Writer's pictureJoe McDowall

Custom Character Rig Breakdown Part 2 - The Face



Rigging the Face

Armed with rigging tools and some principles from part 1 it’s time to breakdown some of the more interesting pieces of the rig starting with the face. For the most part the rig is very standard in terms of the usual controls one might expect on a human rig. I have hands, arms, spine, hips, legs, feet and a head. However I have deviated in a few areas which threw up interesting problems. It is these challenges I would like to spend some time going over.


Eyebrows

The eyebrows on my character are big chunky shapes and exist as seperate meshes to the head they sit on (until joined). My requirements for the eyebrows were as follows:

  • Movement within appropriate area of face

  • Ability to tilt

  • Mechanism to adjust shape for different expressions

For the most part this was a simple translation and rotation which I could do with just a bone on each eyebrow.





I setup a single bone on each eye brow and gave it a maximum weight of 1 across the whole mesh to ensure that it followed the bone completely. To ensure the eyebrow didnt fly off the face or rotate in unnatural ways we can leverage location and rotation limits to ensure the bone acted as expected.





  • Note to self: add a scale limit 🙂


For the custom shape I broke a couple of my rules (already!) and made a bespoke shape just for these controls. I didnt want to over complicate the face with too many controls seeing as there are usually quite a number of different things going on in a facial rig. So I decided to make a subtle control by extruding out an edge loop from the eyebrow and featuring elements that hinted that it could translate and rotate.





For the last requirement I added another deformation bone as a child of the eyebrow bone that would inherit the movement and rotation. To enable the shape change on the eyebrow I weight paint in a radial gradient out from the center of the shape. This way any movement of the bone would cause in center of the mesh to deform more than the outside resulting in a C shape bend.





Since I was only altering the shape on one axis, using a Pull shape and limiting the bone to move on the Z axis seemed intuitive as a control.



Shape Name: Pull

Purpose: Many, but commonly used for finger curling





  • Note that when placing a limit constraint on a bone its also import to check the other axis’s and set their value to zero to prevent movement in the wrong direction. I always check Affect Transform as without it sometimes the internal value of the location can keep going up even when the bone doesnt appear to be moving. This can cause weird delays in movement.


Here is a video with the eyebrow control in action:




Eyes

As mentioned previously the eyes on this rig deviate from the norm on how eyes are usually controlled. Normally eyes have like eye sockets, balls and lids. Controls normally involve rotating the eyeballs and closing the eye lids.


In this rig I wanted to handle eyes a little differently. The style of character I had designed didn’t require realistic eyes and instead could swap between a predefined set of eyes not dismilar to a claymation production. I had the choice of handling this in textures with manipulating UV coordinates or by using physical meshes. Chunky features fit more my style so I decided to go with the later approach.






The requirements were:

  • Allow for the selection of different eyes

  • Only show one eye for each side at any given time

  • Allow some movement in the X / Z local axis

  • Provide some basic eye choices with the scope for adding more


The approach:

For this one I think it will be easier to describe with a diagram:

The general gist is that each different eye (deform bone) has a driver that checks to see if the value of the selector matches its own value range. If it does the eye is moved into place. If it doesnt the eye is hidden away. Its worth noting that in the rig I have a selector for each eye so you can have two different eyes in play at one time. When not selected the unchosen eyes are actually sent off to a different position bone inside the head which has a scale of 0. This was to ensure compatibility with Unity when the deform bones are exported.


Let’s break it down step by step.


Step one - Creating the eyes

The first step was to create a number of bones for each different eye that could be on the face. I created:

  • DEF-eye-open_L

  • DEF-eye-open_R

  • DEF-eye-half-closed_L

  • DEF-eye-half-closed_R

  • DEF-eye-closed_L

  • DEF-eye-closed_R

  • DEF-eye-surprised_L

  • DEF-eye-surprised_R

Each of these bones had a mesh attached to them through weight painting exactly as we did above with the eyebrows. Think of it like putting pins on eyes for a Mr Potato head.


Step two - Add position bones

The next step was to position bones (POS) at the locations where the eyes should go. These should be parented to the head.





A third child bone is placed inside the head which I've called POS-hide-objects. This bone has its scale set to 0. Why will become clear in a moment.



With our position bones in place we now just need some mechanism to move a particular eye into the right location. That's where constraints and drivers come into play.



Step three - placing eyes

The bone constraint we are after is called Copy Transform which as the name implies copies the transform of another object (location, rotation and scale). In our case we need two; one for the hidden objects position and one for the visible eye position . I have named these Eye Hold and Eye Selection respectively.


Note: In this case a an alternative would be to use a combination of Copy Location, Copy Rotation, Copy Scale or the Armature constraint depending on what is required.




The Eye Hold constraint above has the target bone set to POS-hide-objects and the Mix option set to "Replace". Replace I believe instructs it to take the new transform as its own.

The Eye Selection constraint above has the target bone set to POS-eye-slot_R (R for right) and the Mix option set to "Replace". I would copy this setup as well for the left side version.


When we enable the Eye Selection (Copy Transform) constraint and disable the Eye Hold one,  the eye bone is moved into place and takes on the position bones position, rotation and scale.



When we swap the constrains and enable the Eye Hold one instead the eye moves inside the head and adopts the bones scale of zero and is effectively hidden.


With this in place we can manually place an eye in the desired spot and copy this solution to all the different eyes for both sides. However it is a bit of paint to have to manually enable and disable constraints.



Tip: Using Blender 4.0 Bone Collections it is a good idea to create a collection for Eye Bones and assign any bone you create for eyes to it to make it easier to select them when you need them. This is of particular importance if you intend to export this to a game engine like Unity as you will need to keyframe location, scale and potentially rotation for these bones any time you switch them.




Step four - the selector

Now it is time to implement a way of selecting different eyes and have them automatically get moved into the right position. For this we can also leverage bones!



The “selector” is made up of two bones. Firstly a parent with a custom mesh shape that serves as the frame and guide to what eye is being selected. Secondly a child bone with a selector shape that is moved up and down to select an eye. The frame shape was created carefully so that each horizontal edge goes up in increments of 0.01.


Tip: Remember that custom shapes can be a mesh or curve or any shape. For this control I thought it would be a good idea to indicate the eye shape in the frame to make it easier to understand for the animator.



By adding a Limit Location constraint to the child bone (the arrow) and setting the Owner to Local Space we can strict movement to within the frame.




Note: The eagle eyed might have spotted in my demo above that the constraint is offset by 0.2 so the range goes from -0.02 to 0.02. This was because I wanted my particular selector to start from the middle by default and to give the experience of dragging down to close the eyes. I have omitted this from the rest of the guide just to keep things simple.



Step six: Drivers

So we have a selector control that we can move but it currently doesnt do anything with the eyes. For that we need to leverage blenders drivers. Let's begin with moving one of our eyes to the right position. For this one I've chosen the DEF-eye-open_R bone. If we go to the bone constraint we have in place to move the bone currently called Eye Selection and right click on the enabled icon shown below we are presented with a menu in which we can Add Driver.




In the Drivers editor window we now see a new driver on our bone: "Enabled (DEF-eye-open_R: Eye Selection)" shown below.



If you are new to the driver editor window you will have to select the driver name and then the Drivers tab on the right to edit it.


As mentioned previously a driver is a way for us to write some code logic to control a property in Blender. In this case we are adding the driver to a Boolean control. The little eye symbol that controls whether or not a constraint is enabled or disabled. The value of this property can only be True (enabled) or False (disabled) and as such our driver needs to return one of these values. If the value returned is True then our bone constraint will be enabled that moves our eye into positon on the face.


In order to make that decision on whether this eye should be selected (moved) or not we need access to our selector control from the previous step. We'll ignore the Expression for now and focus on accessing the control for the eye selection.



We can edit the default Input Variable. Ensure its type is set to Transform Channel denoted by the (X) above. For the Object we select the armature which enables the bone option. For the bone we select the arrow control from the previous step which I've named CON-eye-switcher_R. The value we're interested in is its local Z position. Set Type to Z Location and Space to Local Space. You should see a value at the bottom now that corresponds to the Z location of our control bone. Moving the selector/switcher contol should change this value.



So that's great, but how do we do something with that? Expressions!



Firstly let's rename the input variable from "var" to something like switcherValue ( switcherLocationZ would be a better name). This way we can understand a little more what the purpose of this variable is.


An expression is just a piece of code that executes and returns a value. Remember what was said earlier about this property being a boolean value, so we need to return either True or False from this expression.




This is the expression I've used:


(switcherValue * 100) >= 0 and (switcherValue * 100) < 1


Multiplying the values by 100 is unnecessary here we could use the values from the controls position as is. The scale of the control is so small in my case that it increments in fractions ( steps of 0.01). I like to deal in whole numbers where possible with ID's so I tend to try and scale the values up. By multiplying the value by 100 we can think of our values as so:



Our open eye is the the one at the bottom so should be selected when the value lies between 0 and 1 (but not including 1). When it is, the expression will return True otherwise if the value is 1 or above this expression will return False.


This is enough to make our eye move to position or not based the value is near 0, but how do we ensure that when this value is false that it moves to the position inside the head with scale 0?



We need to add a new driver to the other bone constrain which will move the eye to POS-hide-objects. The expression we want for this one is the inverse of the last. We want this constraint to activate when the switcher position ID anything less than 0 or 1 and above.


An easy way to do this is it use the not() function which will return the opposite value of a boolean expression as follows:


not((switcherValue *  100) >= 0 and (switcherValue * 100) < 1)


And with that we have everything we need to finish our eye selection tool.


Step seven: rinse and repeat


Unfortunately there is a little bit of reptition ahead; we need to copy and tweak our drivers on to each eye we have created. Thankfully if you right click a driver you can choose to copy it and then you can right click on a property and paste a driver which speeds it up. You'll need to tweak the ranges in the expression for each eye so the next one will match on "> 1 and <= 2" etc etc. You will also need to copy these drivers over to the other side for the same eye if you want to have independant control over each eye.



Tip: You can also use the same drivers to hide the eye meshes from viewport and render when they arent being selected by adding them to the respective icons on the right of the Outliner. This can help with performance in Blender.


And we're done on the eyes!


Here is the result:






Mouth


For the mouth I followed the same exact process as the eyes in terms of being able to select a range of different mouths. Lets instead talk about what differs.





I wanted to add a basic control to deform the hair around the mouth to help sell the impression that the character is talking.



This is done by creating a ring of bones that are children to a central mouth bone. I'm using a control bone shown at the bottom with a custom scale shape that controls the scale of the parent mouth bone. This is done with, you guessed it, bone constraints! This time the Copy Scale constraint on the parent mouth bone.





With some careful weight painting on the surrounding hair it was fairly straight forward to deform around the mouth uniformly by scaling the parent. I got this idea from Rigify which has a much more advanced solution for rigging lips.


The beard also has a bone at the bottom so I could fake jaw movement or give the beard some jiggle.


Moustache

A refined gentleman such as this would be lost without proper moustache control!




Each side of the moustache is controlled by 3 bones and a control to control the curl. The 3 bones form a chain with the last bone being a child of the one before it. This is important.



We also want to make sure each bone has the "Inherit Rotation" option checked in the Bone Properties. This will allow each child to inherit the rotation of its parent allowing a curl.



The control bone is given the pull shape and is locked down on all transforms using location, scale and rotation constraints. Except for the local Y location which is given some wiggle room. The fact we're limiting the control between -0.0333 and 0.333 will get mentioned soon.



To keep the moustache controls relative to the head I've used an Armature constraint to pin the control to it.



In order to setup the curl I first had to weight paint the moustache according to all three bones. I made heavy use of the gradient tool here to smoothly transition the deformation of the moustache evenly across the bones.



Next I needed to convert the location movement of the pull control into a rotation on each bone. At this point it's no surprise that we're going to achive this with constraints and drivers!



Each bone gets a Limit Rotation constraint. Which on the surface appears strange. However we can force a bone to take on a certain rotation value by setting both the min and max values. You'll notice that both properties are purple, that's because they are controlled by drivers. And here it is:




We use a Tranform Channel input variable like we have on other drivers to read in the local Y value of the moustache control (right side). We want to then convert that value somehow into an angle. Remember how we briefly noted that the location control has a range limit of between -0.0333 and 0.0333? Well if we multiply that figure by 1000 that gives us 33.3. That is the value I wanted as the Z rotation of each moustache bone.


Note: Our rotatation value has been calculated in degrees but this propertie expects the rotation value in radians. Thankfully its easy to convert using the radians() function.


Note: In the image below we're actually multiplying by -1000 this was to give the right side of the moustache a negative rotation. The left side would be multiplied by 1000 and have a positive rotation.



With each bone inheriting the rotation of its parent this results in a nice curl.



Ears

The last feature of on the head are the ears. Not technically part of the face but we'll cover it here as it's fairly straight forward.



Nothing fancy on this one just a control for the ear that lets me rotate it within some rotational constraints. A very vanilla implementation compared to the others we have already discussed.




Wrapping up

That's everything on the face of it! I hope some of that was useful. In the next part of the series we will look at some other parts of the rig.

22 views0 comments

תגובות


bottom of page