GLSL Programming/Blender/Two-Sided Smooth Surfaces
This tutorial covers two-sided per-pixel lighting (i.e. two-sided Phong shading).
Here we combine the per-pixel lighting discussed in the tutorial on smooth specular highlights with the two-sided lighting discussed in the tutorial on two-sided surfaces.
Shader Coder
editThe required changes to the code of the tutorial on smooth specular highlights are: new properties for the back material, new local variables for the material parameters in the fragment shader, which are set either to the front material parameters or the back material parameters according to gl_FrontFacing
. Also, the surface normal vector is negated in case a back face is rendered. Furthermore, back-face culling has to be deactivated as described in the tutorial on transparency. It's actually quite straightforward. However, keep in mind that Blender apparently never provides different data in gl_FrontMaterial
and gl_BackMaterial
; thus, you have to replace the uniforms in gl_BackMaterial
by user-specified uniforms (see the tutorial on shading in view space).
The vertex shader could look like this:
varying vec4 position;
// position of the vertex (and fragment) in view space
varying vec3 varyingNormalDirection;
// surface normal vector in view space
void main()
{
position = gl_ModelViewMatrix * gl_Vertex;
varyingNormalDirection =
normalize(gl_NormalMatrix * gl_Normal);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
And the fragment shader could be:
varying vec4 position;
// position of the vertex (and fragment) in view space
varying vec3 varyingNormalDirection;
// surface normal vector in view space
void main()
{
vec3 normalDirection = normalize(varyingNormalDirection);
vec4 ambientColor;
vec4 diffuseColor;
vec4 specularColor;
float shininess;
if (gl_FrontFacing)
{
ambientColor = gl_FrontMaterial.emission;
diffuseColor = gl_FrontMaterial.emission;
specularColor = gl_FrontMaterial.specular;
shininess = gl_FrontMaterial.shininess;
}
else
{
ambientColor = gl_BackMaterial.emission;
diffuseColor = gl_BackMaterial.emission;
specularColor = gl_BackMaterial.specular;
shininess = gl_BackMaterial.shininess;
normalDirection = -normalDirection;
}
vec3 viewDirection = -normalize(vec3(position));
vec3 lightDirection;
float attenuation;
if (0.0 == gl_LightSource[0].position.w)
// directional light?
{
attenuation = 1.0; // no attenuation
lightDirection =
normalize(vec3(gl_LightSource[0].position));
}
else // point light or spotlight (or other kind of light)
{
vec3 positionToLightSource =
vec3(gl_LightSource[0].position - position);
float distance = length(positionToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(positionToLightSource);
if (gl_LightSource[0].spotCutoff <= 90.0) // spotlight?
{
float clampedCosine = max(0.0, dot(-lightDirection,
gl_LightSource[0].spotDirection));
if (clampedCosine < gl_LightSource[0].spotCosCutoff)
// outside of spotlight cone?
{
attenuation = 0.0;
}
else
{
attenuation = attenuation * pow(clampedCosine,
gl_LightSource[0].spotExponent);
}
}
}
vec3 ambientLighting = vec3(gl_LightModel.ambient)
* vec3(ambientColor);
vec3 diffuseReflection = attenuation
* vec3(gl_LightSource[0].diffuse) * vec3(diffuseColor)
* max(0.0, dot(normalDirection, lightDirection));
vec3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation
* vec3(gl_LightSource[0].specular)
* vec3(specularColor) * pow(max(0.0,
dot(reflect(-lightDirection, normalDirection),
viewDirection)), shininess);
}
gl_FragColor = vec4(ambientLighting + diffuseReflection
+ specularReflection, 1.0);
}
Summary
editCongratulations, you have reached the end of this short tutorial. We have seen:
- How two-sided surfaces can be rendered with per-pixel lighting.
Further Reading
editIf you still want to know more
- about the shader version for single-sided per-pixel lighting, you should read the tutorial on smooth specular highlights.
- about the shader version for two-sided per-vertex lighting, you should read the tutorial on two-sided surfaces.