Skin shader / subsurface shader

Hi everyone, I’m working on a Realtime 3d project involving an animated character. Touch PBR material is amazing but I would really need to implement some –basic/fake – type of subsurface scattering or translucency, like the Realtime scattering that can be achieved in unity or substance painter. Can someone tell me if it is even possible by outputing pbr glsl and customizing it? I have a basic understanding of GLSL but I can definitely dive into it if necessary, any advice is obviously more than welcomed.
This is a reference on shadertoy:

1 Like

This would indeed be an awesome addition to have access to in Touch :slight_smile:

I think to achieve this, it would be best to not think about the pbr shading stuff quite yet and just successfully generate the thickness surface attribute, and the subsequent SSS calculation from that attribute (and some others):

float CalculateThickness(in vec3 pos, in vec3 norm)
    // Perform a number of samples, accumulate thickness, and then divide by number of samples.
    float thickness = 0.0;
    for(float i = 0.0; i < SSSThicknessSamples; ++i)
        // For each sample, generate a random length and direction.
        float sampleLength = Hash11(i) * SSSSampleDepth;
        vec3 sampleDir = GenerateSampleVector(-norm, i);
        // Thickness is the SDF depth value at that sample point.
        // Remember, internal SDF values are negative. So we add the 
        // sample length to ensure we get a positive value.
        thickness += sampleLength + Scene(pos + (sampleDir * sampleLength)).x;
    // Thickness on range [0, 1], where 0 is maximum thickness/density.
    // Remember, the resulting thickness value is multipled against our 
    // lighting during the actual SSS calculation so a value closer to 
    // 1.0 means less absorption/brighter SSS lighting.
    return clamp(thickness * SSSThicknessSamplesI, 0.0, 1.0);

You’ll notice that this function is tooled specifically for calculating thickness generatively via SDF techniques. To make this work with regular geometry, one would need to calculate the thickness another way. I’ve read some stuff about using the depth map of a light to calculate the thickness at a given point for geometry, this could be another way to do it generatively.

If you don’t mind an offline method for generating a thickness map, packages like substance painter and others can export a thickness map for an object with UVs. So if you’re importing assets you could prepare those maps externally, and load them into the Touch/Shader.

BTW one free tool for generating lots of awesome maps is Xnormal:

In theory the Render() function in BufferC might be all that needs to be paid attention to (though modification is def necessary).

Specifically, this portion seems relevant:

// SSS enabled
vec3  toEye    = -ray.d;
vec3  SSSLight = (normalize(LightPos - position) + norm * SSSDistortion);
float SSSDot   = pow(clamp(dot(toEye, -SSSLight), 0.0, 1.0), SSSPower) * SSSScale;
float SSS      = (SSSDot + SSSAmbient) * thickness * attenuation;
color = albedo * diffuse * SSS;
1 Like

Ok I had some luck with a very very rough draft of an implementation of this SSS model. In truth, I haven’t read much up on it to learn how it’s supposed to be combined with other parts of the lighting equation, but the SSS component is in fact working like in the shader toy :slight_smile: .

I’ve done a pretty naive ADD to combine it with the dimmer values n such in the pbr function Touch has under the hood.

Download the sample project here


I’ll try to revisit this and eventually upload a more official plug and play component for this later. What would be ideal, is to leave TouchDesigner’s PBR shader alone, and create this SSS effect as a separate “pass” that can be simply composited overtop with an add TOP etc.


Dear Lucasm,
thank you so much for your help!
I’m speechless and infinitely grateful
Sorry for the late response but I spent the weekend trying to dissect your work to better understand it and adapt to my needs… basically I made my long time postponed glsl Homeworks
I can’t say that I understood everything but I think I’m close, at least I hope so :sweat_smile:
As I’m working with multiple animated fbx I think the best workflow for me would be to keep everything in a single material and send the sss effect to a separate render buffer. I would try in the next days to implement it starting from your glsl code.
I attach one image that shows how adding the sss pass (bottom image) to the pbr pass (top right image) makes the shading of the body way more interesting (top left ), at least I think so. It’s nothing physically based cause the effect comes from a light placed behind the body but until sss would show up in the PBR MAT at least we have something to play with.
I will try to figure out if the sss pass can somehow react to the normal map of the model, cause I think the output would greatly benefit from that.
All the best!

1 Like

I upload a small variation to Lucasm project.
Clicking on the “2 Buffers” button, the Sss effect is passed to a render buffer so the sss effect can be easily tweaked in post.
The Sss effect is now calculated for every light in the scene and its contribution can be adjusted with sliders.
I added the slots for having the GLSL Mat properly read all pbr maps (except height) so the Material can be easily used for testing with other models.
Unfortunately, shadows are not working, so far I didn’t have luck in fixing the issue.
If you look into the code and find something messy be tolerant, I’m not a coder and it’s just a couple of days of me playing with GLSL.


P.S.: the “SSS” and “PBR+SSS” buttons come from the previous version, they just show the contribution of light n.1 and don’t take into account the PBR maps I added. (3.6 MB)

1 Like

Looking good! Ya when I fiddled with shadows it occured it me that simply compositing the SSS effect with shadows doesn’t really respect the light bleed. I think shadows require a more integrated approach, because it would not just be a mask, but affect where the actual scattering happens.