Per-Axis Vector Blending (How To Deform Any Mesh Span Into A Flat Line)
PLEASE NOTE: I HAVE A POST IN THE WORKS ABOUT A BETTER TECHNIQUE ON HOW TO DEFORM THE CHEEK. I'M LEAVING THIS POST UP IN CASE OTHER PEOPLE FIND THIS TECHNIQUE HELPFUL IN OTHER PARTS OF THEIR RIG.
So that title is a mouthful so let me explain what it is I am talking about. When you're deforming the crease of the eyelid or the cheek, you want to be able to deform that span so that it is horizontal and lies parallel to line of the closed eyelid. If you have "The Art of Moving Points" by Brian Tindall then go to the "Squint/Cheek Zone" to see what I am talking about. If you don't have that eBook then take a look at this reel to see what it is I am talking about https://vimeo.com/199443666 (the part with the cheek is at 3:00). Here are two screenshots from the reel that show what I am talking about.
So as you can see the span of the cheek is moved up and the crease is rotated up so that they both create lines that are parallel to the eyelid. So there are three ways to achieve this result.
#1 Try to split up the weights manually:
So the usual solution is to create three joint segments that rotate around the eye center. The issue with this setup is that it is impossible to split up the weight between the three influences so that you get the horizontal line when rotating the joints up, and get nice compression and stretching of the spans when rotating the joints left and right. If you weight the joints in order to get nice left and right deformations you will end up with a 3 shaped span instead of a nice horizontal line.
You can solve this by using corrective blendShapes for the up and down motion, or you could use single joint if that provides enough control for the animator.
#2 Per Axis Vector Blending:
So for every vertex on the span, we can find the vector from the center of the eye to that vert. These aren't global vectors, they are vectors in a user defined local-space, that will come from a locator located at the center of the eye and rotated to aim the z axis of the locator at the center of the line where the upper and lower eyelids meet. Sometimes the local space is the same as global space, but if the eyes are angled outward the locator will aim slightly out to the side.
Then we create guide joints that aim along some of these vectors. To have 3 controls, we will use the first vector, the last vector, and the middle vector, and two between those. Keep in mind this image is in 2D but we are dealing with 3D vectors. Now it's important that the guide vectors have certain properties, all the vectors that lie between two guide vectors, have X,Y,and Z components that lie between the X,Y,and Z components of the guide vectors.
So now we are going to blend the guide vectors to create the other vectors. If we just did a linear blend of all three axis together (like with a blendColors node) we would only be able to create vectors that lie on a straight line (between the two vectors). So like the title of this post suggests we are going to blend each axis separately. Rather than eyeballing all the blending weights you're going to want to use some simple math:
desired output = input * atrributesBlender + input * (1 - atttributesBlender)
attributesBlender = (output - input)/(input - input)
So then if we take the guide vectors and line them up within a single plane, all the blended vectors will also lie in that plane, and because we are aiming joints along these vectors all those joints will lie in that plane in nice straight line. Then we just skin those joints to the mesh and we're done.
Keep In Mind:
To be honest, the way that this splits up the weighting results in some ugly up and down shapes as you can see in the video. The best solution is to have two separate skinClusters one for the UD motion and one for the LR motion both with different weights, but this can be very hard to manage in Maya. I'd like to explore it more moving forward, because some of the workarounds like this that I use sometimes have become overly complicated. If I do come up with a better work flow for splitting weights I will be sure to make a post about it.