@MarkTheEchidna: Tangent-space normal mapping is mostly a simple operation. The hardest part about it, is effectively writing everything to function in tangent space instead of world space. The basic math in GLSL speak using the per-vertex light direction (assuming model space coordinates) is:
CODE
mat3 rotation = mat3(a_tangnet, cross(a_tangnet, gl_normal), a_normal);
vec3 rotatedVal = rotation * u_lightDirection;
GLSL will take care of the matrix multiplication required to rotate your vector into tangent-space. For world space to tangent space, the only difference is instead of an object space normal, you use a world space normal. Please note however, it's best that you only use either world space or object space normals with the accompanying world or object space vectors. Otherwise, you'll get incorrect results every time.
Another solution, is to transform your tangent space normal map into world space in your fragment program, and just apply all of your operations from there:
Fragment program
CODE
varying vec3 v_worldNormal;
varying vec3 v_tangent;
varying vec3 v_bitangent;
//assume the usual varies here such as texcoords and the like
uniform sampler2D u_normalMap;
void main() {
vec3 normal = texture2D(u_normalMap, v_texcoord.xy) * 2 - 1; //need to unpack our normal map to the -1 to 1 range before we can use it
normal = v_tangent * normal.x + v_bitangent * normal.y + v_worldNormal * normal.z; //convert our tangent space normal to world space
gl_FragColor = dot(normal, vec3(0.0, 1.0, 0.0)); //accumulate lighting from above in world space
}
Something to be wary of however, conversion of tangent space normals to world space isn't the most performance savvy operation in the world, and given that most people in the community are likely still stuck with only Shader Model 2 capable hardware, you're likely to want to avoid converting to world space normals too much.
EDIT: Come to think of it, you could actually use render targets to effectively composite the scene together a lá deferred rendering, while staying well within SM2 constraints (which for reference, SM2 supports about 64 fragment instructions, vs. SM3's roughly 512).
This post has been edited by Gen: 08 April 2011 - 06:57 AM