Real-time skin shader

Started by
10 comments, last by TimAnderegg 12 years, 9 months ago
Hey hey.

Thought I'd stop by and post something that I've been working on for the last while. A skin shader that aims to make realistic faces while maintaining high performance and avoiding texture/image space diffusion; I'm running this in CryEngine2 so I'm pretty limited in what I can do. I can't do lightmap passes so texture space diffusion is out of the question (and it's slow) and I can't make custom post-process stages for image space diffusion, thus making both methods nigh impossible.

So for those of you who are interested in a cheap alternative to diffusion approximations and the likes, read on!

I've been working around these limits by approximating the core concepts of various rendering models (basically just creating layers and faking blurs heh). Here's a render from a few days ago (a few things have changed but the shader is mostly the same):



Head model available here.

I'm using the Kelemen Szirmay-Kalos for my BRDF as proposed by NVIDIA. I'm using 4 terms for my specular model (instead of the 1 used on that page), using 4 beckmann distributions (saved to a texture) and weighing them at the at the end with a simple dot product.

Specularity:



For the actual subsurface scattering I do a mix of things. I use a multi-layer system (like those found in popular modeling programs like Maya or 3ds Max, for example the MentalRay FastSkin shader). I also use blended normals for local normal-based diffusion (Naughty Dog "Character Lighting and Shading") to get some nice scattering effects as well as some of my own little tweaks (a closer rgb relationship in blended normals to keep red bleeding lower for a pleasing look).

The texture maps for each layer (subdermal and epidermal) can be done by hand, or they can be generated by the shader (merely a few simple color operations on the diffuse texture, and sampling a mip-map for the subdermal texture for a blurry effect). The overall tint can be changed through the shader's settings. The approximated textures generated by the shader are good enough for regular use, but pre-made textures have the benefit of having more control and artistic freedom. The model used in the example images uses a generated epidermal map and a pre-made subdermal texture (for back scattering, see below).

Texture Maps (yes, my translucency texture sucks :P):


For backscattering (yes, the shader supports back scattering, although it's primitive and will probably need changing eventually) I simply light the model using a wrapped negative NdotL value (opposite of the light) and mask it using a translucency texture (placed in the alpha channel of the subdermal texture). It's not physically correct but it does the job just fine. I'd love to get translucent shadow maps working but limitations are stopping that from happening and I haven't found any other decent alternatives yet (if someone knows of any or has any ideas, please let me know! :D).

Back scattering (mind the jittery shadows):



For each layer I calculate a different lighting result. For the diffuse texture, I simply calculate blended normals as usual, using a standard NdotL. For the epidermal layer, I do the same but with a slightly more wrapped NdotL, and for the subdermal I use an even more wrapped NdotL. This is to simulate the layers being "blurred" so that when weighed together at the end of the shader, they create a proper subsurface effect. The weights for each layer can be controlled in the shaders properties.

To properly scatter shadows I do something very similar to blended normals. For each layer, after blending the normals, I blend between a hard shadow and a soft shadow, allowing a red-bleed to occur on the shadows. The strength of sharpening and softening for each channel is changed for each layer (diffuse is very hard, epidermal is softer and subdermal is very soft). Rather than performing blurs on the shadow maps which can result in poorer performance, I cheat... big time. I simply run pow() for each channel (green & blue are merged in both blended normals and blended shadows), giving a harder shadow. This basically hardens and shrinks the radius of the green/blue channels while the red channel remains nice and smooth. The result is desaturated a bit so it doesn't come off as too red. Since I'm doing this in CryEngine2 I decided to pre-blur the shadow maps since jittering can get ugly after hardening the shadows (it's merely a screen texture, so blurring it only needs to be done once: I'm doing a mere 4 tap blur (4 texture samples surrounding the original). I've had some good results using this method but it still needs a bit of work.

Bleeding shadows (100% diffuse vs 100% epidermal/subdermal (no diffuse))



The final effects are detail bump mapping (basically a tiled detail normal map layered over the original to give the impression of more detail), rim lighting and the option to change the skin's melanin amount. These are pretty standard effects really (the melanin shader is just lerping between 1 and the diffuse texture multiplied by it's luminance using a melanin value).

Here's a comparison between standard lambertian lighting with a physically based blinn-phong specular (Naty Hoffman), the skin shader without SSS and then with SSS.



Performance is great, it's ~the same as a standard fully-featured material shader, you probably wouldn't notice much impact on performance if you swapped it for say, a metal shader.

Compare that to the performance it takes to render texture-space diffusion which is orders of magnitude slower than this, I'd say I've hit a good mark. It's not as physically accurate as NVIDIA's Head Demo, but looking at the results and the simplicity of the shader, I think I'll be able to close the gap eventually. I hope to push forward and obtain something more correct, but I think this is a good step forward.

To anyone who read: thanks for reading. ;) Here's a last minute screen-capture.



[Edited by - Styves on October 18, 2010 2:22:46 AM]
Advertisement
Very impressive. I've seen many different cheap skin techniques that use one or two of the elements you're talking about, but it's really promising to see how good things can look with all of that used together.
definitely looks good, goes to show you can do it using 400% of the cpu power you dont have or you can go for a cheaper model that nearly looks just as good, approximating the reality instead of blindly emulating real light when the computer doesnt have a chance at doing it fast enough.
great!

The model and diffuse are nice definitely... as long as you get that part right possibly you only need a small amount of sss tech to go with it.
Thanks very much for sharing your details, and pics of the results! Nice work.

We did a similar effect on Wii, where we lit using a epidermal and dermal texture, and simply lerp'ed the 2 results based on the lighting angle. This implementation did kind-of use the blurred texture-space lighting idea, but instead of rendering the lighting of the actual object, we rendered the lighting for a 32*32px sphere-map, and used that to look up the lighting result.

In our texture-space implementation for other platforms, we also used the translucency-map idea for back-faces, which did work ok in practice.
Certainly going to have a better look at this when I'm in the need of a skin shader. Don't want to be rude, but have you though about writing a paper? Thanks for sharing!

Rick
Heh, well since you asked, I decided to give it a go. It's not the greatest thing in the world but it should give some insight to how it was done (a.k.a it has code :P). First powerpoint doc I've made since high-school lol (actually, it wasn't even powerpoint, it was some knockoff program).

Uploaded it to mediafire since I didn't know where to upload it. Hopefully I didn't miss anything :P.

Download

I'd just like to note that I'm still working on this so things I say/do in the paper might change later and I might mark those techniques as obsolete. If I do I'll be sure to post about it. :B

Edit(October 19, 2010): I made some changes to the paper and optimized some of the code. If you've already looked at it, have another look. :)

Edit(October 21, 2010): Fixed some mistakes in the paper. Nothing major.

[Edited by - Styves on October 21, 2010 6:56:06 PM]
Thanks! Too busy to had a detailed look, but that paper looks good! Placing it into my mini library :)

Rick

Uploaded it to mediafire since I didn't know where to upload it. Hopefully I didn't miss anything :P.

Download


Nice results!

By the way, the link is not working. If you still have the paper, could you please upload it again? I am sure it will be useful for many people.

EDIT: The link is working now. Excelent material! Thanks for sharing.
Very impressive!
Impressive work! It's amazing to see the results you got by using a fairly simple technique like that one.

It's because of work like yours that I keep coming to this forum! It's awesome when people like you just come here and present bright solutions to complex problems that are important for videogame development.

This topic is closed to new replies.

Advertisement