Friday 8 February 2013

Face Rigging Tutorial, Non-Linear Shape Blending

Welcome to CG From Space!  This is my blog about CG art.

Lately, I've been learning a lot about rigging faces with shapes.  Here are some shots of my latest project:



My next picture is an example of a common problem when rigging with face shapes.  It's what happens when you turn on the blink shape to a value of half.

Ew.
Shape blending moves the affected vertices in a straight line, and so they crash through the round eyeball mesh, instead of flowing around it.

This tutorial will show you how to do a shape blending technique called "quadratic interpolation" which allows the affected vertices to move along a curve.  Best of all, this technique also maintains a firm control over the eyelid's shape.

I'll be explaining how to use this technique in Blender 2.65, but it could work in Softimage, Maya, or any other 3D software with blend shapes.  I'll be assuming you already have a general understanding of how to use Blender.

This technique involves quite a bit of math, so I'm going to break this tutorial into two parts: part one will provide basic step-by-step, math-free instructions;  part two will explain the math for those who are interested.

PART ONE – The Math-Free Instructions






Below is a video containing a straight-up demonstration how this technique works.
In it, I rig up a blink for my character "Alto" from my animation Trio Animato








If you can't handle the video's wacky klesmer music, you can read the step-by-step instructions below.


Open up Blender, and load one of your characters.  Make sure that there are enough loops surrounding the eyes, for them to be modified into a blink shape.

1) First of all, we need to create a blink shape.  This shape should allow the upper eyelid to wrap snugly around the entire eyeball.  This is what mine looks like:



2) Create a half-blink shape.  This shape is what your eyeball will look like when it's exactly half-way through a blink. I usually create this shape by setting the "blink" shape to 0.5 and creating a new shape with "New Shape From Mix" in the shape panel's dropdown menu.  I then pull the new shape's vertices outwards until they're resting on the surface of the eye.  Make sure nothing is clipping into the eye.



3)  Create a driver for each shape.  Right click the slider for each shape and pick "Add Driver".


4) The drivers will be mixing these shapes together in some wacky combinations.  In order to give your drivers the freedom they need, change the max and min values for both shapes to +4 and -4.


5)  We need an input for our driver, so let's create a custom parameter, and name it "Blink".  I prefer to store my custom parameters on the "Object" panel.  You can store this parameter on any object that you like, but for now it's probably easiest to store it on your character's face object.


6) We're ready to set up the drivers.  Open the driver editor, and find the shape drivers.  Both drivers should be using the "Averaged Value" of a "Single Property".  Next, set the driver to read in the value of the custom parameter (named "Blink") that we just created.  This can be tricky if you're new to using drivers.  If you stored your custom parameter in an object, select "Obj" in the dropdown menu labeled "Type".  In the next box, select the name of the object that stores your custom parameter.  Lastly, in the box labeled "Path" you need to tell the driver which custom parameter to read (because an object can store more than one custom paramter).  I typed ' ["Blink"] ' in that box (including the [ ] and " ") to tell it to use the custom parameter named Blink.



7) Put a second order polynomial modifier on your driver.  In the above picture, you'll notice that I set the "Blink" driver's polynomial constants to 0.0, -1.0 and 2.0.  Part 2 of my tutorial explains where I got these numbers, but for now you'll have to just believe me.  The "Half Blink" shape driver should also be a second order polynomial with constants 0.0, 4.0 and -4.0.  When you're done, click "Update Dependencies" and Blender will be ready to drive your shapes.

So, that's it!  We're done!  Try it!  Slide the "Blink" parameter between values of 0 and 1, and your character's eye should now blink along a smooth, curved path.











PART TWO – An Awesome Pile-Of-Math

Quadratic Splines

This technique uses quadratic splines, so we first need to understand what they are, and how they work.

What is a quadratic spline?  Well, imagine that you draw three points in space, P1 = (x1, y1), P2 = (x2, y2), and P3 = (x3, y3).  You want to connect these three points using a parabola.  You can do this with a quadratic spline.

That's cool, but how do they work?  Well...

We will define a curve P(t).  We want this curve to trace a parabola as t increases.
We also want the curve to pass through the three points we drew.  Say we wanted P(t) such that:

P1 = P(0)
P2 = P(1/2)
P3 = P(1)

In other words, as t varies from 0 to 1/2 to 1, P(t) will trace a parabola, hitting the points P1, P2, and P3 along the way.  These three points are knows as the "control points".  What does P(t) look like mathematically?  Well, something like this:

P(t) = a2t2 + a1t + a0

The curve P(t) is a second order polynomial in t, with three constants, a2, a1, and a0.  If can we find the values of these constants, we will have everything we need to know for P(t) to work.  We can solve for the constants by plugging in our previously chosen t values.

P1 = P(0)
   = a2(0)2 + a1(0) + a0
   = a0
P2 = P(1/2)
  = a2(1/2)2 + a1(1/2) + a0
  = a2(1/4) + a1(1/2) + a0
P3 = P(1)
  = a2(1)2 + a1(1) + a0
  = a2 + a1 + a0

We now have three equations and three unknowns.  If you remember your high school algebra, it shouldn't be hard to solve for a2, a1, and a0.  If you try to solve it yourself, you should get:

a0 =  P1
a1 =  -P3 + 4P2 - 3P1
a2 =  2P3 - 4P2 + 2P1

Great!  If we take these values and stick them into our definition of P(t), our problem is solved.

P(t) = (2P3 - 4P2 + 2P1 )t2 + (-P3 + 4P2 - 3P1)t + P1


This equation describes exactly what we wanted: a parabolic curve which passes through points P1, P2, and P3 as t varies between 0 and 1.  Try it!  Set t to a value between 0 and 1 and see what you get.

Shapes:

Using the above equation, we can parametrize the movement of a single point P(t) along a parabola.  Shape blending in 3D software can be manipulated in the exactly the same way.  The only difference is that instead of blending a single point along a parabola, we're blending hundreds of points each along their own individual paths.  Say you had three shapes, S1, S2, and S3, and you wanted them to blend together such that each vertex follows a parabola? Well, we just do the same as we did above.

Define:

S(t) = a2t2 + a1t + a0

and we'll constrain S(t) such that it uses three "control shapes",

S1 = S(0)
S2 = S(1/2)
S3 = S(1)

After solving for a2, a1 and a0, we should have the analogous result:

S(t) = (2S3 - 4S2 + 2S1 )t2 + (-S3 + 4S2 - 3S1)t + S1

Now let's re-arrange this equation into a more useful form.

S(t) = S3(2t2 - t) + S2(-4t2 + 4t) + S1( 2t2 - 3t + 1)

Let's take a moment to realize the sheer awesomeness this result contains!  Say you have a character with three shapes.  If you take shape #1 and scale it with the value ( 2t2 - 3t + 1), scale shape #2 with the value (-4t2 + 4t), and scale shape #3 by value (2t2 - t), you will get S(t)!  Changing the parameter t between 0 and 1 will cause the character to morph smoothly from shape #1, then #2, then #3!  Simply create a custom parameter to store the value of t, and use it as an input for the above scaling factors.

You can now understand where I got my number settings for my shape drivers.  I wanted to interpolate from the basis shape (S1), through the "half-blink" shape (S2), and end at the "blink" shape (S3).   I therefore multiplied the "blink" shape with a second order polynomial of the custom parameter, using coefficients 0, -1 and 2, to produce (2t2 - t + 0).  My "halfBlink" shape was likewise scaled with (-4t2 + 4t).  As for S1, my basis shape, I didn't need to do anything because the basis shape is the undeformed shape, which is mathematically equivalent to the "zero" shape.  S(t) therefore doesn't depend on the values that multiply S1.

So, that's it!  You've used quadratic spline interpolation to get a curved path for your shapes!
I hope this has been useful!