Home Unreal Engine

SSS Shaders

I was curious to know how people here handle skin in UDK. I tried out the SSS feature of the DX11 renderer, and frankly I thought it looked like a pathetic excuse for SSS. That plus the DX11 renderer gives me tons of issues.

Anyway, here is what I'm working on at the moment. I'm interested to know how it looks so far. It doesn't use multiple pre-blured normal maps like some (http://forums.epicgames.com/threads/732232-DT3D-UDK-Skin-Shader) implementation have. I gave it a try but the results didn't seem to be any better than what I'm currently using, which doesn't require any additional normal maps or convolutions.

Basic Blinn-Phong material on the left, and custom SSS material on the right.

sss_comparison.jpg
http://s20.postimage.org/woses0h4b/sss_comparison.jpg

Replies

  • Santewi
    Options
    Offline / Send Message
    What does your material setup look like?
  • Ace-Angel
    Options
    Offline / Send Message
    Ace-Angel polycounter lvl 12
    I always thought of D3TD's Skin shader more as of "Blended Normals" on steroids rather then a special SS solution.

    If you blend his setup that with a blue color, you will literally get the same effect, you could also try Mipping the Normal Map, but CustomTexture nodes don't like Normal Maps.

    As for 'more robust' SSS solution, I don't think it's that possible in UDK, especially with the shadow issues and lack of control for light attenuation, unless you use a Proxy-Mesh for Shadow casting, but last I checked, UDK doesn't have such a solution in place.
  • lpcstr
    Options
    Offline / Send Message
    Ace-Angel wrote: »
    I always thought of D3TD's Skin shader more as of "Blended Normals" on steroids rather then a special SS solution.

    That's essentially all mine is too, only I found that multiple normal maps make no major difference.

    Ace-Angel wrote: »
    If you blend his setup that with a blue color, you will literally get the same effect, you could also try Mipping the Normal Map, but CustomTexture nodes don't like Normal Maps.

    I tried that already. The results were similar. Memory usage was obviously smaller than D3TD's, but instruction count was higher. I had no issues with the CustomTexture node.

    What exactly are the shadow and attenuation issues you are talking about?
    Santewi wrote: »
    What does your material setup look like?

    I'll post my technique as soon as I'm comfortable with the results and have had time to optimize it.
  • Ace-Angel
    Options
    Offline / Send Message
    Ace-Angel polycounter lvl 12
    Not sure if it's an issue on my behalf, but also it could be because I'm using a Gooch setup in my SSS, in UDK, the shadows can get pretty jaggy when self-shadowing is enabled, and certain parts of the skin, based upon light distance (even close by) should start losing their 'scatter' effect, especially if you're using half-lambert kind of solutions, where you get shadow-acne.

    Also, I tend to use PointLights in my scenes alot, and these can very easily shoot through the mesh and light up unwanted parts of your model, especially if you're using an inverted lambert term for the transmission effect on skin.

    So it's a more technical issue thing, where due to graphical fidelity being sometimes gimped in UDK will cause unwanted effects I guess, not sure if it's simply my UDK having a lower setting or not.
  • Santewi
    Options
    Offline / Send Message
    lpcstr wrote: »
    I'll post my technique as soon as I'm comfortable with the results and have had time to optimize it.

    Oh alright. What's the instruction could like at the moment?
  • lpcstr
    Options
    Offline / Send Message
    Still messing around trying to get some decent looking skin. I wish I could say I knew what I was doing. Still interested to hear how other people handle it.

    sss_skin.jpg
    http://s20.postimage.org/k249dhmzv/sss_skin.jpg
  • lpcstr
    Options
    Offline / Send Message
    Nobody has any experience/interest in the subject?

    Here are some new images.

    (2 pointlights + constant ambient term modulated by occlusion map)
    http://s20.postimage.org/prz8tb7ot/skin1.jpg

    (1 pointlight + constant ambient term modulated by occlusion map)
    http://s20.postimage.org/vu6vjsw4t/skin2.jpg

    (1 pointlight + diffuse and specular irradiance environment maps)
    http://s20.postimage.org/jgu1cw6gd/skin3.jpg
  • pior
    Options
    Offline / Send Message
    pior grand marshal polycounter
    I think that first image with two point lights is right on the money! Looks much better than the example shown on the UDK page. Your solution seems to nicely avoid the red smear that tend to appear on many cheap skin shaders.

    Very cool!

    On a side note : where can one download that generic head ?
  • lpcstr
    Options
    Offline / Send Message
    pior wrote: »
    On a side note : where can one download that generic head ?

    http://www.ir-ltd.net/infinite-3d-head-scan-released/
  • pior
    Options
    Offline / Send Message
    pior grand marshal polycounter
    Thanks! And good work on the material again, can't wait to see more examples!
  • lpcstr
    Options
    Offline / Send Message
    For those who are interested, here is a description of my current setup.

    First I take my normal map into Photoshop and create two versions of it. To one I apply a Gaussian blur (8 pixels wide in my case), to the other I apply a high-pass filter (same diameter as before). This splits the normal maps into two parts, the first being the low frequency part (contains the large scale details), and the second being the high frequency part (contains high frequency details like pores and tiny wrinkles). Then I store the X and Y part of the low frequency normal into the R and G channels of my normal map, and I store the X and Y part of the high frequency normal into the B and A channels. I do this so that I can import the texture as TC_VectorDisplacmentMap, which gives me the highest quality possible (normals are important!) To get the low frequency normal, simply sample R and G as your X and Y and then derive Z. For the high frequency normal, use RG + BA, then derive Z. The real reason I do this will be more apparent when I explain the environment mapping portion.

    Analytical lighting:

    I calculate diffuse as max(0, pow((dot(LightVector, Normal) + 1) * 0.5, 3)). Once with low frequency normals and once with high frequency normals. Then I lerp between them using the coefficient (0.2, 0.8, 1.0). Many people simply use the geometry normals for the low frequency portion. This will yield bad results if your normal map contains anything but high frequency details.

    For the transmission effect I use DICE's technique for cheap translucency (http://www.slideshare.net/colinbb/colin-barrebrisebois-gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurfacescattering-look-7170855). I calculate the translucency map with xNormal the same way they describe.

    For my specular I use Cook-Torrance with a Beckmann distribution. I used the code from here to implement it (http://content.gpwiki.org/index.php/D3DBook:%28Lighting%29_Cook-Torrance).

    Environment map lighting:

    Getting HDR environment maps to work inside UDK was a major pain. It would be less difficult if you weren't so concerned with quality. Anyway, I take an equirectangular environment map (you can find them online for free in EXR or HDR format) and I import it into HDRShop (v1.0 is free for non-commercial use) and I use the convolution filter to generate separate diffuse and specular versions. I then use a simple program written in C to convert the images into 32-bit RGBE encoded Bitmap files. The problem with using RGBE is that any type of interpolation of the texture will cause artifacts. To prevent that I first import the textures as TC_VectorDisplacmentMap. Any sort of compression would cause artifacts. Next I set mipmaps to none, as the mip levels would be generated using linear interpolation. Then finally I set the texture filtering mode to nearest neighbor. Again, linear filtering would cause artifacts. Then, in my HDR sampling material function, I convert the incoming reflection vector into a 2D UV coordinate to look up in the equirectangular texture. The code for that looks like this:
    const float PI = 3.14159265359;
    float2 longlat = float2(atan2(dir.y,dir.x),acos(dir.z));
    return longlat / float2(2.0*PI,PI);
    
    The reason I use a 2D equirectangular texture instead of a cubmap is because with the filtering mode set to nearest neighbor, the environment map would look awful. By using a 2D texture, I can implement custom linear interpolation in the shader. Linear interpolation samples the four nearest texels and then lerps between them based on the texture coordinate. By implementing it in shader, you can take each of the four texture samples and convert them from RGBE to RGB and then perform the interpolation, giving you the correct results every time.

    For the specular portion of the environment lighting I simply sample my specular irradiance environment map with the reflection vector, and then multiply it by a fresnel and specular term.

    For the diffuse portion, I sample the diffuse irradiance environment map twice. Once using the low frequency normal, and again with the high frequency normal. Then I lerp between them the same way I did for the diffuse. Then I multiply it by the diffuse texture and the occlusion texture. Since the diffuse irradiance environment map is generated using a simple cosine filter (normal lambert) it doesn't have the same benefit of smoothing out the lighting like the modified lambert I use for the analytical portion. This makes the normals look like they are too harsh (IMO) so what I do is I multiply the high frequency portion of the normal map by 0.5 before combining it with the low frequency. This gives it a softer look without messing up the large scale details.

    You can get transmission from the environment by sampling the environment map using the negated viewing angle. The problem is that you get transmission at times when there should be none, and it is quite obvious. This is actually a problem for the specular reflection as well. I'm looking into ways of solving this, if anyone has ideas I'd like to hear them.

    I think that covers everything. This post sort of turned out longer than I anticipated, hopefully it makes sense and doesn't ramble.
  • King Mango
    Options
    Offline / Send Message
    King Mango polycounter lvl 10
    I think you've got the colors and transitions really good. But what about sub surface occlusion? Specifically the nose is a real problem for me.

    [EDIT]What about using the blur tool in ps to selectively blur your normal maps so you can reduce the sss on the bridge of the nose? Or even just masking back in 'hard' normals after your blur filter?
  • TheMetaMorphe
    Options
    Offline / Send Message
    TheMetaMorphe polycounter lvl 12
    Reading your thread made me want to work again on my skin shader.

    Great work !
  • lpcstr
    Options
    Offline / Send Message
    I have since replaced my diffuse lighting twice. First, I tried a energy conserving wrapped lighting implemented like this:
    saturate((dot(N, L) + w) / ((1 + w) * (1 + w)));
    
    Where w = 0 is essentially normal Lambert and w = 1 is essentially half Lambert, only energy conserving. This produces pretty nice results, but I have since switched to Pre-Integrated Skin Shading. It's not energy conserving (something for me to explore) but the results look pretty good already.

    http://s20.postimage.org/pgscu5ktn/update3.jpg
  • TheMetaMorphe
    Options
    Offline / Send Message
    TheMetaMorphe polycounter lvl 12
    Pretty good :).

    Is it the same Pre-Integrated Skin Shading from Eric Penner ?

    I'm using the one from Penner but I've got difficulties with the tonemapper. When I use the Pre-Integrated Skin Shading, the skin is extremly shiny.

    Did you use a custom tone mapper ?

    edit :

    good read : http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
  • lpcstr
    Options
    Offline / Send Message
    Pretty good :).

    Is it the same Pre-Integrated Skin Shading from Eric Penner ?

    I'm using the one from Penner but I've got difficulties with the tonemapper. When I use the Pre-Integrated Skin Shading, the skin is extremly shiny.

    Did you use a custom tone mapper ?

    No, I'm just using the default settings.
  • migusan76
    Options
    Offline / Send Message
    migusan76 polycounter lvl 18
    Great work lpcstr! Funny I stumbled across this while doing some research, but I agree with TheMetaMorphe about being inspired to get back to working on some skin shading materials.
Sign In or Register to comment.