Monday, December 9, 2013

Better Skin Shading in Unity

In CG (both games and movies), there's something called the "uncanny valley". This refers to the trend where as human rendering becomes more and more realistic, at some point it suddenly stops looking realistic and instead starts looking creepy.
One technique seeing a rise in the AAA games industry is known as "sub surface scattering". I won't go into details, but the basics of it are this: light enters the skin, is scattered about, absorbed depending on how far it travels, and then exits (because skin is not actually opaque - it's semi-transparent).
There are many ways to go about this effect. There's texture space diffusion, screen space scattering, and (my personal favorite) pre-integrated skin shading.
What is pre-integrated skin shading? It's a very fast method of faking subsurface scattering, by baking the results into a lookup texture, and using the lookup texture as a lighting ramp.
Why is it important? Why can't we just use our old friends, Lambertian lighting and Blinn-Phong for shading our characters?
Because they end up looking like mannequins. Remove the skin color, and you're left with a statue.
Let's take a look at some pictures.
Here's the arm of a soldier character I'm using for testing. On the left, Blinn-Phong shading is used. On the right, pre-integrated skin shading is used.
I see the one on the left far too often, especially in indie/Unity games.

So how is it done?
First, you have your lookup texture. Here's your "realistic" lookup texture, using the lookup texture generator on an AltDevBlogADay article about it.
The U coordinate is lighting (from 0 to 1), while the V coordinate is basically skin depth (from 0 at the bottom, allowing no light through, to 1 at the top, allowing the most light through)
I don't like this one, however. In my opinion, realism is always trumped by what looks right. And to me, fiddling with that texture in Photoshop is what looks right.
Exaggerated? Definitely. Looks good? Hell yeah.
The article as linked above uses DDX and DDY to calculate skin depth, as well as tex2Dlod to sample a "blurred" version of the normal map. This imposes a lot of restrictions on the shader - too many, in my opinion.
Instead of using DDX/DDY, I'm going to use a plain old texture map for the same purpose. This puts the burden on the artist to paint skin depth, which is fine by me since that affords a lot more control over the final result anyway (and makes our shader more versatile, since a skin depth of zero gives us regular Lambert shading - we might give a character's clothes a value of zero, for instance). I'm also going to skip the tex2Dlod step, just using the normal map as-is.
And what does this give me? A realistic character shader which is miles ahead of your average blinn-phong shading, and is efficient enough to run on mobile platforms without breaking a sweat.

Hope this gives some people some ideas.
Happy gaming, everybody.

No comments:

Post a Comment