GLSL translation of this fake sub-surface scattering shader by InvalidPointer.

I’m always amazed when these things actually work…

Vertex Shader:

//////////////////////////// // SUB-SURFACE SCATTER VS // //////////////////////////// /* -------------------------- SubScatter Vertex Shader: Fake sub-surface scatter lighting shader by InvalidPointer 2008. Found at http://www.gamedev.net/community/forums/topic.asp?topic_id=481494 HLSL > GLSL translation toneburst 2008 -------------------------- */ // Set light-position uniform vec3 LightPosition; // Varying variables to be sent to Fragment Shader varying vec3 worldNormal, eyeVec, lightVec, vertPos, lightPos; void subScatterVS(in vec4 ecVert) { lightVec = LightPosition - ecVert.xyz; eyeVec = -ecVert.xyz; vertPos = ecVert.xyz; lightPos = LightPosition; } //////////////// // MAIN LOOP // //////////////// void main() { worldNormal = gl_NormalMatrix * gl_Normal; vec4 ecPos = gl_ModelViewProjectionMatrix * gl_Vertex; // Call function to set varyings for subscatter FS subScatterVS(ecPos); //Transform vertex by modelview and projection matrices gl_Position = ecPos; //Forward current texture coordinates after applying texture matrix gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; }

Fragment Shader:

//////////////////////////// // SUB-SURFACE SCATTER FS // //////////////////////////// /* -------------------------- SubScatter Fragment Shader: Fake sub-surface scatter lighting shader by InvalidPointer 2008. Found at http://www.gamedev.net/community/forums/topic.asp?topic_id=481494 HLSL > GLSL translation toneburst 2008 -------------------------- */ // Variables for lighting properties uniform float MaterialThickness; uniform vec3 ExtinctionCoefficient; // Will show as X Y and Z ports in QC, but actually represent RGB values. uniform vec4 LightColor; uniform vec4 BaseColor; uniform vec4 SpecColor; uniform float SpecPower; uniform float RimScalar; //uniform sampler2D Texture; // Varying variables to be sent to Fragment Shader varying vec3 worldNormal, eyeVec, lightVec, vertPos, lightPos; float halfLambert(in vec3 vect1, in vec3 vect2) { float product = dot(vect1,vect2); return product * 0.5 + 0.5; } float blinnPhongSpecular(in vec3 normalVec, in vec3 lightVec, in float specPower) { vec3 halfAngle = normalize(normalVec + lightVec); return pow(clamp(0.0,1.0,dot(normalVec,halfAngle)),specPower); } // Main fake sub-surface scatter lighting function vec4 subScatterFS() { float attenuation = 10.0 * (1.0 / distance(lightPos,vertPos)); vec3 eVec = normalize(eyeVec); vec3 lVec = normalize(lightVec); vec3 wNorm = normalize(worldNormal); vec4 dotLN = vec4(halfLambert(lVec,wNorm) * attenuation); //dotLN *= texture2D(Texture, gl_TexCoord[0].xy); dotLN *= BaseColor; vec3 indirectLightComponent = vec3(MaterialThickness * max(0.0,dot(-wNorm,lVec))); indirectLightComponent += MaterialThickness * halfLambert(-eVec,lVec); indirectLightComponent *= attenuation; indirectLightComponent.r *= ExtinctionCoefficient.r; indirectLightComponent.g *= ExtinctionCoefficient.g; indirectLightComponent.b *= ExtinctionCoefficient.b; vec3 rim = vec3(1.0 - max(0.0,dot(wNorm,eVec))); rim *= rim; rim *= max(0.0,dot(wNorm,lVec)) * SpecColor.rgb; vec4 finalCol = dotLN + vec4(indirectLightComponent,1.0); finalCol.rgb += (rim * RimScalar * attenuation * finalCol.a); finalCol.rgb += vec3(blinnPhongSpecular(wNorm,lVec,SpecPower) * attenuation * SpecColor * finalCol.a * 0.05); finalCol.rgb *= LightColor.rgb; return finalCol; } //////////////// // MAIN LOOP // //////////////// void main() { gl_FragColor = subScatterFS(); }