GLSL Programming/GLUT/Glossy Textures

This tutorial covers per-pixel lighting of partially glossy, textured surfaces.
It combines the shader code of the [[Template:BOOKNAME/GLUT/Textured Spheres|tutorial on textured spheres]] and the [[Template:BOOKNAME/GLUT/Smooth Specular Highlights|tutorial on smooth specular highlights]] to compute per-pixel lighting with a material color for diffuse reflection that is determined by the RGB components of a texture and an intensity of the specular reflection that is determined by the A component of the same texture. If you haven't read the [[Template:BOOKNAME/GLUT/Textured Spheres|tutorial on textured spheres]] or the [[Template:BOOKNAME/GLUT/Smooth Specular Highlights|tutorial on smooth specular highlights]], this would be a very good opportunity to read them.
Gloss Mapping
The [[Template:BOOKNAME/GLUT/Lighting Textured Surfaces|tutorial on lighting textured surfaces]] introduced the concept of determining the material constant for the diffuse reflection by the RGB components of a texture image. Here we extend this technique and determine the strength of thea specular reflection by the A (alpha) component of the same texture image. Using only one texture offers a significant performance advantage, in particular because an RGBA texture lookup is under certain circumstances just as expensive as an RGB texture lookup.
If the “gloss” of a texture image (i.e. the strength of the specular reflection) is encoded in the A (alpha) component of an RGBA texture image, we can simply multiply the material constant for the specular reflection with the alpha component of the texture image. was introduced in the [[Template:BOOKNAME/GLUT/Specular Highlights|tutorial on specular highlights]] and appears in the specular reflection term of the Phong reflection model:
If multiplied with the alpha component of the texture image, this term reaches its maximum (i.e. the surface is glossy) where alpha is 1, and it is 0 (i.e. the surface is not glossy at all) where alpha is 0.

Shader Code for Per-Pixel Lighting
The shader code is a combination of the per-pixel lighting from the [[Template:BOOKNAME/GLUT/Smooth Specular Highlights|tutorial on smooth specular highlights]] and the texturing from the [[Template:BOOKNAME/GLUT/Textured Spheres|tutorial on textured spheres]]. Similarly to the [[Template:BOOKNAME/GLUT/Lighting Textured Surfaces|tutorial on lighting textured surfaces]], the RGB components of the texture color in textureColor is multiplied to the ambient and diffuse lighting.
In the particular texture image to the left, the alpha component is 0 for water and 1 for land. However, it should be the water that is glossy and the land that isn't. Thus, with this particular image, we should multiply the specular material color with (1.0 - textureColor.a). On the other hand, usual gloss maps would require a multiplication with textureColor.a. (Note how easy it is to make this kind of changes to a shader program.)
The vertex shader is then:
attribute vec3 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
varying vec4 texCoords; // the texture coordinates
uniform mat4 m, v, p;
uniform mat3 m_3x3_inv_transp;
void main()
{
vec4 v_coord4 = vec4(v_coord, 1.0);
mat4 mvp = p*v*m;
position = m * v_coord4;
varyingNormalDirection = normalize(m_3x3_inv_transp * v_normal);
texCoords = v_coord4;
gl_Position = mvp * v_coord4;
}
And the fragment shader becomes:
varying vec4 position; // position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection; // surface normal vector in world space
varying vec4 texCoords; // the texture coordinates
uniform mat4 m, v, p;
uniform mat4 v_inv;
uniform sampler2D mytexture;
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, 0.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.8, 0.8, 1.0),
vec4(1.0, 1.0, 1.0, 1.0),
5.0
);
void main()
{
vec3 normalDirection = normalize(varyingNormalDirection);
vec3 viewDirection = normalize(vec3(v_inv * vec4(0.0, 0.0, 0.0, 1.0) - position));
vec3 lightDirection;
float attenuation;
vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
(asin(texCoords.z) / 3.1415926 + 0.5));
// unusual processing of texture coordinates
vec4 textureColor = texture2D(mytexture, longitudeLatitude);
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(textureColor);
vec3 diffuseReflection = attenuation
* vec3(light0.diffuse) * vec3(textureColor)
* 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(frontMaterial.specular) * (1.0 - textureColor.a)
// for usual gloss maps: "* textureColor.a"
* pow(max(0.0, dot(reflect(-lightDirection, normalDirection), viewDirection)), frontMaterial.shininess);
}
gl_FragColor = vec4(ambientLighting + diffuseReflection + specularReflection, 1.0);
}
The texture and sphere have to be set up as described in the [[Template:BOOKNAME/GLUT/Textured Spheres|tutorial on textured spheres]].
A useful modification of this shader for the particular texture image above, would be to set the diffuse material color to a dark blue where the alpha component is 0.
Shader Code for Per-Vertex Lighting
As discussed in the [[Template:BOOKNAME/GLUT/Smooth Specular Highlights|tutorial on smooth specular highlights]], specular highlights are usually not rendered very well with per-vertex lighting. Sometimes, however, there is no choice because of performance limitations. In order to include gloss mapping in the shader code of the [[Template:BOOKNAME/GLUT/Lighting Textured Surfaces|tutorial on lighting textured surfaces]], the fragment shader should be replaced with this code:
varying vec3 diffuseColor;
// the interpolated diffuse Phong lighting
varying vec3 specularColor;
// the interpolated specular Phong lighting
varying vec4 texCoords;
// the interpolated texture coordinates
uniform sampler2D mytexture;
void main(void)
{
vec2 longitudeLatitude = vec2((atan(texCoords.y, texCoords.x) / 3.1415926 + 1.0) * 0.5,
(asin(texCoords.z) / 3.1415926 + 0.5));
// unusual processing of texture coordinates
vec4 textureColor =
texture2D(mytexture, longitudeLatitude);
gl_FragColor = vec4(diffuseColor * vec3(textureColor)
+ specularColor * (1.0 - textureColor.a), 1.0);
}
Note that a usual gloss map would require a multiplication with textureColor.a instead of (1.0 - textureColor.a).
Summary
Congratulations! You finished an important tutorial about gloss mapping. We have looked at:
- What gloss mapping is.
- How to implement it for per-pixel lighting.
- How to implement it for per-vertex lighting.
Further Reading
If you still want to learn more
- about per-pixel lighting (without texturing), you should read [[Template:BOOKNAME/GLUT/Smooth Specular Highlights|tutorial on smooth specular highlights]].
- about texturing, you should read [[Template:BOOKNAME/GLUT/Textured Spheres|tutorial on textured spheres]].
- about per-vertex lighting with texturing, you should read [[Template:BOOKNAME/GLUT/Lighting Textured Surfaces|tutorial on lighting textured surfaces]].