GLSL Programming/GLUT/Two-Sided Smooth Surfaces

From Wikibooks, open books for an open world
Jump to navigation Jump to search
Rendering of Cayley's nodal cubic surface using different colors on the two sides of the surface.

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[edit | edit source]

The 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.

The vertex shader could look like this:

attribute vec4 v_coord;
attribute vec3 v_normal;
varying vec4 position;  // position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection;  // surface normal vector in world space
uniform mat4 m, v, p;
uniform mat3 m_3x3_inv_transp;

void main()
{
  position = m * v_coord;
  varyingNormalDirection = normalize(m_3x3_inv_transp * v_normal);

  mat4 mvp = p*v*m;
  gl_Position = mvp * v_coord;
}

And the fragment shader could be:

varying vec4 position;  // position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection;  // surface normal vector in world space
uniform mat4 m, v, p;
uniform mat4 v_inv;

struct lightSource
{
  vec4 position;
  vec4 diffuse;
  vec4 specular;
  float constantAttenuation, linearAttenuation, quadraticAttenuation;
  float spotCutoff, spotExponent;
  vec3 spotDirection;
};
lightSource light0 = lightSource(
  vec4(0.0,  1.0,  2.0, 1.0),
  vec4(1.0,  1.0,  1.0, 1.0),
  vec4(1.0,  1.0,  1.0, 1.0),
  0.0, 1.0, 0.0,
  180.0, 0.0,
  vec3(0.0, 0.0, 0.0)
);
vec4 scene_ambient = vec4(0.2, 0.2, 0.2, 1.0);

struct material
{
  vec4 ambient;
  vec4 diffuse;
  vec4 specular;
  float shininess;
};
material frontMaterial = material(
  vec4(0.2, 0.2, 0.2, 1.0),
  vec4(1.0, 0.0, 0.0, 1.0),
  vec4(1.0, 1.0, 1.0, 1.0),
  100.0
);
material backMaterial = material(
  vec4(0.2, 0.2, 0.2, 1.0),
  vec4(0.0, 0.0, 1.0, 1.0),
  vec4(1.0, 1.0, 1.0, 1.0),
  100.0
);

void main()
{
  vec3 normalDirection = normalize(varyingNormalDirection);
  vec4 ambientColor;
  vec4 diffuseColor;
  vec4 specularColor;
  float shininess;
  
  if (gl_FrontFacing)
    {
      ambientColor = frontMaterial.ambient;
      diffuseColor = frontMaterial.diffuse;
      specularColor = frontMaterial.specular;
      shininess = frontMaterial.shininess;
    }
  else
    {
      ambientColor = backMaterial.ambient;
      diffuseColor = backMaterial.diffuse;
      specularColor = backMaterial.specular;
      shininess = backMaterial.shininess;
      normalDirection = -normalDirection;
    }
  
  vec3 viewDirection = normalize(vec3(v_inv * vec4(0.0, 0.0, 0.0, 1.0) - position));
  vec3 lightDirection;
  float attenuation;
  
  if (0.0 == light0.position.w) // directional light?
    {
      attenuation = 1.0; // no attenuation
      lightDirection = normalize(vec3(light0.position));
    } 
  else // point light or spotlight (or other kind of light) 
    {
      vec3 positionToLightSource = vec3(light0.position - position);
      float distance = length(positionToLightSource);
      lightDirection = normalize(positionToLightSource);
      attenuation = 1.0 / (light0.constantAttenuation
			   + light0.linearAttenuation * distance
			   + light0.quadraticAttenuation * distance * distance);
      
      if (light0.spotCutoff <= 90.0) // spotlight?
	{
	  float clampedCosine = max(0.0, dot(-lightDirection, light0.spotDirection));
	  if (clampedCosine < cos(radians(light0.spotCutoff))) // outside of spotlight cone?
	    {
	      attenuation = 0.0;
	    }
	  else
	    {
	      attenuation = attenuation * pow(clampedCosine, light0.spotExponent); 
	    }
	}
    }
  
  vec3 ambientLighting = vec3(scene_ambient) * vec3(ambientColor);
  
  vec3 diffuseReflection = attenuation 
    * vec3(light0.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(light0.specular) * vec3(specularColor) 
	* pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), shininess);
    }
  
  gl_FragColor = vec4(ambientLighting + diffuseReflection + specularReflection, 1.0);
}

Summary[edit | edit source]

Congratulations, 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[edit | edit source]

If you still want to know more


< GLSL Programming/GLUT

Unless stated otherwise, all example source code on this page is granted to the public domain.
Back to OpenGL Programming - Lighting section Back to GLSL Programming - GLUT section