3) Substract original bump from second (H0-H1). This leads to brightened (B) and darkened (D) areas.
Evaluate Fragment Color Cf
• Cf = (L*N) × Dl × Dm
• (L*N) ~ (Fd + (H1-H0))
• Dm × Dl is encoded in surface texture Ct. Could control Dl seperately, if you're clever. (we control it using OpenGL-Lighting!, rem.: Jens)
• Cf = (Fd + (H0-H1) × Ct
We're Not Quite Done Yet. We Still Must:
• Build a texture (using a painting program, rem.: Jens)
• Calculate texture coordinate offsets (ds,dt)
• Calculate diffuse Factor Fd (is controlled using OpenGL-Lighting!, rem.: Jens)
• Both are derived from normal N and light vector L (in our case, only (ds,dt) are calculated explicitly!, rem.: Jens)
• Now we have to do some math
Conserve Textures!
• Current multitexture-hardware only supports two textures! (By now, not true anymore, but nevertheless you should read this!, rem.: Jens)
• Bump Map in ALPHA channel (not the way we do it, could implement it yourself as an exercise if you have TNT-chipset rem.: Jens)
• Maximum bump = 1.0
• Level ground = 0.5
• Maximum depression = 0.0
• Surface color in RGB channels
• Set internal format to GL_RGBA8 !!
Rotate Light Vector Into Normal Space
• Need Normal coordinate system
• Derive coordinate system from normal and "up" vector (we pass the texCoord directions to our offset generator explicitly, rem.: Jens)
• Normal is z-axis
• Cross-product is x-axis
• Throw away "up" vector, derive y-axis as cross-product of x– and z-axis
• Build 3×3 matrix Mn from axes
• Transform light vector into normal space.(Mn is also called an orthonormal basis. Think of Mn*v as to "express" v in means of a basis describing tangent space rather than in means of the standard basis. Note also that orthonormal bases are invariant against-scaling resulting in no loss of normalization when multiplying vectors! rem.: Jens)
Use Normal-Space Light Vector For Offsets
• L´ = Mn × L
• Use L´x, L´y for (ds,dt)
• Use L´z for diffuse factor! (Rather not! If you're no TNT-owner, use OpenGL-Lighting instead, since you have to do one additional pass anyhow!, rem.: Jens)
• If light vector is near normal, L´x, L´y are small.
• If light vector is near tangent plane, L´x, L´y are large.
• What if L´z is less than zero?
• Light is on opposite side from normal
• Fade contribution toward zero.
Calculate Vectors, Texcoords On The Host
• Pass diffuse factor as vertex alpha
• Could use vertex color for light diffuse color
• H0 and surface color from texture unit 0
• H1 from texture unit 1 (same texture, different coordinates)
• ARB_multitexture extension
• Combines extension (more precisely: the NVIDIA_multitexture_combiners extension, featured by all TNT-family cards, rem.: Jens)
Combiner 0 Alpha-Setup:
• (1-T0a) + T1a – 0.5 (T0a stands for "texture-unit 0, alpha channel", rem.: Jens)
• (T1a-T0a) maps to (-1,1), but hardware clamps to (0,1)
• 0.5 bias balances the loss from clamping (consider using 0.5 scale, since you can use a wider variety of bump maps, rem.: Jens)
• Could modulate light diffuse color with T0c
• Combiner 0 rgb-setup:
• (T0c * C0a + T0c * Fda – 0.5 )*2
• 0.5 bias balances the loss from clamping
• scale by 2 brightens the image
Though we're doing it a little bit different than the TNT-Implementation to enable our program to run on ALL accelerators, we can learn two or three things here. One thing is, that bump mapping is a multi-pass algorithm on most cards (not on TNT-family, where it can be implemented in one 2-texture pass.) You should now be able to imagine how nice multitexturing really is. We'll now implement a 3-pass non-multitexture algorithm, that can be (and will be) developed into a 2-pass multitexture algorithm.
By now you should be aware, that we'll have to do some matrix-matrix-multiplication (and matrix-vector-multiplication, too). But that's nothing to worry about: OpenGL will do the matrix-matrix-multiplication for us (if tweaked right) and the matrix-vector-multiplication is really easy-going: VMatMult(M,v) multiplies matrix M with vector v and stores the result back in v: v:=M*v. All Matrices and vectors passed have to be in homogenous-coordinates resulting in 4×4 matrices and 4-dim vectors. This is to ensure conformity to OpenGL in order to multiply own vectors with OpenGL-matrices right away.
// Calculates v=vM, M Is 4x4 In Column-Major, v Is 4dim. Row (i.e. "Transposed")
void VMatMult(GLfloat *M, GLfloat *v) {
GLfloat res[3];
res[0]=M[ 0]*v[0]+M[ 1]*v[1]+M[ 2]*v[2]+M[ 3]*v[3];
res[1]=M[ 4]*v[0]+M[ 5]*v[1]+M[ 6]*v[2]+M[ 7]*v[3];
res[2]=M[ 8]*v[0]+M[ 9]*v[1]+M[10]*v[2]+M[11]*v[3];
v[0]=res[0];
v[1]=res[1];
v[2]=res[2];
v[3]=M[15]; // Homogenous Coordinate
}
Here we'll discuss two different algorithms. I found the first one several days ago under:
http://www.nvidia.com/marketing/Developer/DevRel.nsf/TechnicalDemosFrame?OpenPage
The program is called GL_BUMP and was written by Diego Tбrtara in 1999.
It implements really nice looking bump mapping, though it has some drawbacks.
But first, lets have a look at Tбrtara's Algorithm:
1. All vectors have to be EITHER in object OR world space
2. Calculate vector v from current vertex to light position
3. Normalize v
4. Project v into tangent space. (This is the plane touching the surface in the current vertex. Typically, if working with flat surfaces, this is the surface itself).
5. Offset (s,t)-coordinates by the projected v's x and y component
This looks not bad! It is basically the Algorithm introduced by Michael I. Gold above. But it has a major drawback: Tбrtara only does the projection for a xy-plane! This is not sufficient for our purposes since it simplifies the projection step to just taking the xy-components of v and discarding the z-component.
But his implementation does the diffuse lighting the same way we'll do it: by using OpenGL's built-in lighting. Since we can't use the combiners-method Gold suggests (we want our programs to run anywhere, not just on TNT-cards!), we can't store the diffuse factor in the alpha channel. Since we already have a 3-pass non-multitexture / 2-pass multitexture problem, why not apply OpenGL-Lighting to the last pass to do all the ambient light and color stuff for us? This is possible (and looks quite well) only because we have no complex geometry, so keep this in mind. If you'd render several thousands of bump mapped triangles, try to invent something new!
Furthermore, he uses multitexturing, which is, as we shall see, not as easy as you might have thought regarding this special case.
But now to our Implementation. It looks quite the same to the above Algorithm, except for the projection step, where we use an own approach: