An OpenGL GLSL Normal and Specular Mapping Shader
This is a shader written in the OpenGL Shading Language (GLSL). It implements bump mapping using a normal map. In order to achieve more realistic lighting and more interesting material properties, this shader also does specular mapping. Below you will find the GLSL source code, as well as instructions on how it can be used.

General
Normal mapping is a technique that uses a texture in which the normals per fragment are encoded in the texture's colours. These normals replace vertex normals and are used for per-fragment lighting. Normal maps add a lot of detail to an otherwise flat surface, without increasing the vertex-count of a model.
Shader Inputs
Tangent, Binormal and Normal
Normals in a normal map are stored relative to the texture's coordinate system. This system is called tangent space. In order to translate eye space coordinates (e.g. light and eye positions) into tangent space, you need the correct tangent space matrix. This matrix (or its vectors) is calculated outside the shader; usually when first loading a model. Detailed articles on how to derive the tangent space matrix can be found here http://jerome.jouvie.free.fr/OpenGl/Lessons/Lesson8.php and here http://www.blacksmith-studios.dk/projects/downloads/tangent_matrix_derivation.php.
The tangent space matrix consists of a tangent, a binormal and a normal. All three have to be passed to the shader for each vertex. The normal is passed via glNormal(...). The other two are passed as shader attributes named tangent and binormal. Alternatively the binormal can be computed in the shader as the cross product of the normal and the tangent.
Textures
The fragment shader requires three textures. A diffuse textures that contains the traditional texture. A normal map that contains the normals and a specular map that determines specular highlights.
Diffuse Texture

A plain white diffuse texture, to see the effect of the shader more clearly.
Normal Map
Normal maps encode XYZ coordinates of normals as RGB colours. A colour ranging from 0 to 255 is mapped to a coordinate ranging from -1.0 to +1.0. This shader expects tangent-space normal maps (as opposed to object-space normal maps).

A tangent-space normal map with circular raised bumps.
Specular Map
The specular map is used to determine the strength and colour of specular reflection. The original specular colour of the light will be modulated by the colour in this texture.

A specular map with green, white and no specular reflection.
Shininess
Shininess is a uniform value used by the fragment shader to influence specular highlights. The higher this value the sharper and smaller specular reflections will be.
Light
Finally the light's parameters have to be passed to the shader. This shader uses a uniform variable named light of the included type lightDataType. In theory GLSL has built-in variables that contain the OpenGL light state (gl_LightSource). However some graphics cards' drivers do not set these values properly. To run on a broader range of hardware, this shader uses its own uniform variable for light. The shader expects the light's position to be in eye space coordinates, i.e. multiplied by a model view matrix.
The Vertex Program
struct lightDataType {
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 position; // Position in eye coordinates
};
uniform lightDataType light;
attribute vec3 tangent;
attribute vec3 binormal;
varying vec3 tbnDirToLight; // Direction to light in tangent space
varying vec3 tbnHalfVector; // Half vector in tangent space
void main(void) {
// Vertex location
gl_Position = ftransform();
// Texture coordinates
// Multiplication with texture matrix can be omitted if default (identity matrix) is used. To
// use the texture matrix comment the first and uncomment the second line.
gl_TexCoord[0] = gl_MultiTexCoord0;
// gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
// Tangent space vectors (TBN)
// The binormal can either be passed as an attribute or calculated as cross(normal, tangent).
vec3 mvTangent = gl_NormalMatrix * tangent;
vec3 mvBinormal = gl_NormalMatrix * binormal;
vec3 mvNormal = gl_NormalMatrix * gl_Normal;
// vec3 mvBinormal = cross(mvNormal, mvTangent);
// Vertex coords from eye position
vec3 mvVertex = vec3(gl_ModelViewMatrix * gl_Vertex);
// Eye direction from vertex, for half vector
// If eye position is at (0, 0, 0), -mvVertex points to eye position from vertex. Otherwise
// direction to eye is: eyePosition - mvVertex
vec3 mvDirToEye = -mvVertex;
vec3 tbnDirToEye;
tbnDirToEye.x = dot(mvDirToEye, mvTangent);
tbnDirToEye.y = dot(mvDirToEye, mvBinormal);
tbnDirToEye.z = dot(mvDirToEye, mvNormal);
// Light direction from vertex
vec3 mvDirToLight = vec3(light.position) - mvVertex; // For positional lights lights
tbnDirToLight.x = dot(mvDirToLight, mvTangent);
tbnDirToLight.y = dot(mvDirToLight, mvBinormal);
tbnDirToLight.z = dot(mvDirToLight, mvNormal);
// Half-vector for specular highlights
tbnHalfVector = normalize(tbnDirToEye + tbnDirToLight);
}
The Fragment Program
struct lightDataType {
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 position;
};
uniform lightDataType light;
uniform float shininess; // Shininess exponent for specular highlights
uniform sampler2D diffuseTexture;
uniform sampler2D normalMap;
uniform sampler2D specularMap;
varying vec3 tbnDirToLight; // Direction to light in tangent space
varying vec3 tbnHalfVector; // Half vector in tangent space
void main(void) {
// Base colour from diffuse texture
vec4 baseColour = texture2D(diffuseTexture, gl_TexCoord[0].xy);
// Uncompress normal from normal map texture
vec3 normal = normalize(texture2D(normalMap, gl_TexCoord[0].xy).xyz * 2.0 - 1.0);
// Depending on the normal map's format, the normal's Y direction may have to be inverted to
// achieve the correct result. This depends - for example - on how the normal map has been
// created or how it was loaded by the engine. If the shader output seems wrong, uncomment
// this line:
// normal.y = -normal.y;
// Ambient
vec4 ambient = light.ambient * baseColour;
// Diffuse
// Normalize interpolated direction to light
vec3 tbnNormDirToLight = normalize(tbnDirToLight);
// Full strength if normal points directly at light
float diffuseIntensity = max(dot(tbnNormDirToLight, normal), 0.0);
vec4 diffuse = light.diffuse * baseColour * diffuseIntensity;
// Specular
vec4 specular = vec4(0.0, 0.0, 0.0, 0.0);
// Only calculate specular light if light reaches the fragment.
if (diffuseIntensity > 0.0) {
// Colour of specular reflection
vec4 specularColour = texture2D(specularMap, gl_TexCoord[0].xy);
// Specular strength, Blinn–Phong shading model
float specularModifier = max(dot(normal, normalize(tbnHalfVector)), 0.0);
specular = light.specular * specularColour * pow(specularModifier, shininess);
}
// Sum of all lights
gl_FragColor = clamp(ambient + diffuse + specular, 0.0, 1.0);
// Use the diffuse texture's alpha value.
gl_FragColor.a = baseColour.a;
}
Rendered Output
A quad rendered with the shader, the three textures above and a grey light source:

Downloads
Download the shader source and example textures (~60KB)
A ShaderDesigner project is included in the download. ShaderDesigner is a free GLSL IDE.
Additional Enhancements
Many additional enhancements are possible, in order to create more interesting effects. For example you could add multi-light support, simple self-shadowing and an emissive light map. This way you get better looking models without increasing the number of vertices.

The same shader with multiple lights, self-shadowing and an emissive light map.
Click here to see the enhanced version in action
(requires Java 5+ and an OpenGL 2.0-capable graphics card)
I just spent the night puzzling and fumbling through an HLSL/C# version of this with a tutorial no less. Great job getting this done by yourself! This is difficult material to learn on your own...
- reply
Submitted by Ivan (not verified) on 18. May 2009 - 18:26.Hah, just saw the date on your post. I guess we were both up late last night? :)
- reply
Submitted by Ivan (not verified) on 18. May 2009 - 18:27.Per dimagrire
Sfondi gratis
Giochi mario
- reply
Submitted by Anonymous (not verified) on 9. March 2010 - 20:58.ack nm, i read May instead of March.
- reply
Submitted by Ivan (not verified) on 18. May 2009 - 18:37.Just attribute it to bad design. If it's too easy to confuse different entities, they're not very well designed. Roman gods are to blame :D
I started with tutorials and examples, too. In hindsight that was a waste of time. At some point I just ignored them and worked through the math myself. Once you know the math, there are so many great shaders you can come up with. Fixed function mode suddenly becomes very boring.
- reply
Submitted by Ciardhubh on 22. May 2009 - 12:53.Thanks, for the good articles ...I am very intiresting..
- reply
Submitted by evden eve nakliyat (not verified) on 19. December 2009 - 22:07.hey I didn't agree at all... The design are not bad as compared to many worst one.... it is ok .....
Thanks
Virtual Sex Games
- reply
Submitted by John (not verified) on 5. March 2010 - 12:48.To run on a broader range of hardware, it's interesting that this shader uses its own uniform variable for light. The shader expects the light's position it's good to be in eye space coordinates, multiplied by a model view matrix.
- reply
Submitted by replica bags (not verified) on 20. February 2010 - 10:25.Replica Handbags
Fake Handbags
Louis Vuitton Handbags
Replica Louis Vuitton Handbags
Louis Vuitton Handbags
Replica Louis Vuitton Handbags
Fake Louis Vuitton Handbags
Louis Vuitton Replica Handbags
GUCCI Handbags
GUCCI Replica Handbags
replica GUCCI Handbags
fake GUCCI Handbags
Chanel Handbags
replica Chanel Handbags
chanel bags
Prada Handbags
replica Prada Handbags
fake Prada Handbags
Prada replica Handbags
Miu Miu Handbgs
replica Miu Miu Handbgs
Miu Miu replica Handbgs
fake Miu Miu Handbgs
- reply
Submitted by replica Handbags (not verified) on 23. February 2010 - 12:39.Can Acai berry select scams arrive? Acai berry reviews fiddles below acai berry select. Acai berry reviews censors acai berry select without the quality powder. A rock offers the coat. Acai berry reviews bakes the success around a fiscal corridor. The dash mark bowls acai berry select.
- reply
Submitted by Anonymous (not verified) on 27. February 2010 - 18:42.zhjchv0201
Replica Handbags
Replica Handbags
Replica Handbags
Replica Handbags
Replica Handbags
Dior Handbags
Chloe handbags
Thomas Wylde Handbags
Replica Handbags
Valentino handBags
replica Hermes Belts
Marni Handbags
Replica Belts
Replica Handbags
Louis Vuitton handbags
Loewe Handbags
Bottega Veneta Handbags
Replica Versace Handbags
Gucci handbags
Prada Handbags
Hermes Handbags
Jimmy Choo Handbags
Fianl Sales Handbags
Fake handbags
Versace Handbags
Miu Miu handbags
Replica Belts
Balenciaga Handbags
Lancel Handbag
Louis Vuitton handbags
- reply
Submitted by lange (not verified) on 1. February 2010 - 4:32.cheapest kamagra tablets
- reply
Submitted by Anonymous (not verified) on 25. February 2010 - 16:55.10luoyuejun0209
Replica Louis Vuitton
Replica Watches
replica handbags
Louis vuitton handbags
Replica Handbags
replica watches
Wholesale Replica Handbags
Omega Replica Watches
replica Omega
Omega Classic Watches
Omega CO AXIAL Watches
Omega CONSTELLATION Watches
Omega DEVILLE Watches
Omega Museum Watches
Omega OLYMPIC COLLECTION Watches
Omega OMEGAMANIA Watches
Omega Railmaster Watches
Omega Seamaster Watches
Omega SPEEDMASTER Watches
Chanel Handbags
Replica Chanel
Chanel bags
Chanel Replica Handbags
Replica Chanel Bags
Chanel Purses
Cheap Chanel Bags
Chanel Watches
Chanel Wallets
Chanel Sunglasses
Chanel Jewelry
Chanel Replica Jewelry
Chanel Scarves
Chanel Silk Scarf
Cheap Chanel Scarves
replicat tiffany
tiffany jewelry
Replica Bags
Hermes Handbags
Replica Purses
Replica Mobile
replica scarf
Burberry scarves
Hermes scarves
Ugg boots
Ugg boots
Replica handbags
Replica Gucci
Gucci Handbags
Chanel handbags
Hermes handbags
adidas shoes, air jordans
Wholesale nike shoes, adidas shoes Blogs
air jordans, jordan fusions
air jordans, jordan fusions
jordan fusions,Chanel Shoes
Chanel Shoes, Christian Louboutin Shoes
Replica Fashion Handbags Blogs
Christian Louboutin Shoes, Manolo Blahnik Shoes
Manolo Blahnik Shoes, Shoe Trends
This week in shoes, spring 21 shoes
- reply
Submitted by replica handbags (not verified) on 9. February 2010 - 5:50.bowtrol reviews
acai berry select
alta white reviews
breast actives
digest it
provillus reviews
- reply
Submitted by Anonymous (not verified) on 19. February 2010 - 18:59.