[Entropy starts his series on graphical effects]

Bump Mapping First off, I should mention that this is the first of a hopefully regular set of tutorials explaining different graphical effects. Bumpmapping is the simulation of a lightsource moving over a bumpy surface. The bumps "reflect" the light at different angles, producing the effect shown in BUMP.EXE. The only real trick to it is knowing how the light reflects. First off, we need to know that the illumination of a point varies with the cosine of the angle between the point's normal of the surface's normal (it just is, don't ask me exactly why). We can assume that the normal to the surface is (0, 0, 1) because it sits along the x-y plane with no z-variation. And if we use unit vectors (unit vectors have magnitudes of 1) for the point normals, we know that the cosine of the angle is equal to the z-co-ordinate of the point normal, since, on a unit circle, cosine is the same as the x-co-ordinate, and we are using the angle between the surface normal and the point normal, not the tangent to the surface and the point normal (think of it as a unit circle where an angle of 0 points straight up, and rotates clockwise and the angle increases). So now we just need to know the normal to the point, which is approximated by: normal.x = texture(y, x - 1) - texture(y, x + 1) normal.y = texture(y - 1, x) - texture(y + 1, x) But we still need to know the z-co-ordinate of the normal. Since we are using unit vectors: normal.z = 1 - SQR(normal.x ^ 2 + normal.y ^ 2) The rest is (relatively) easy. We'll want to create a lightmap so this runs at tolerable speed. Assuming a gradient palette where color 0 is black and color 255 is white, we can use the following: DIM light(63, 63) AS INTEGER FOR y = 0 TO 63 FOR x = 0 TO 63 light(y, x) = 255 - 4 * SQR(x ^ 2 + y ^ 2) IF light(y, x) < 0 THEN light(y, x) = 0 NEXT x NEXT y This works because z = 1 - SQR(x ^ 2 + y ^ 2), and we need to multiply it by 256 to get the whole palette (z = 256 - 256 * SQR(x ^ 2 + y ^ 2)), and finally because x and y range from 0 to 63, not from 0 to 1, so we have to divide them by 64: z = 256 - 256 * SQR((x / 64) ^ 2 + (y / 64) ^ 2) We can factor out the 64s to get: z = 256 - 4 * SQR(x ^ 2 + y ^ 2) One final thing to factor in is the distance from the lightsource, which is almost too easy. Just add to the normals of each point the distance from the light: normal.x = (x - light.x) + normal.x normal.y = (y - light.y) + normal.y Then just lookup light(normal.y, normal.x) and you've got the color. One final thing to mention is that you can usually precalculate the point normals, unless the texture will change or something: FOR y = 0 TO ymax FOR x = 0 TO xmax normals(y,x).x=texture(y,x-1)-texture(y,x+1) normals(y,x).y=texture(y-1,x)-texture(y+1,x) NEXT x NEXT y An example routine is as follows: FOR y = 0 TO ymax FOR x = 0 to xmax lx = ABS(x - lightx + normals(y, x).x) ly = ABS(y - lighty + normals(y, x).y) IF lx < 64 AND ly < 64 THEN col = light(ly, lx) ELSE col = 0 END IF PutPixel x, y, col NEXT x NEXT y One final note is that, to make the bumps appear smaller, you can divide the precalculated normals by some number. That's all for bumpmapping. If you have any requests for the subject of my next tutorial, e-mail me. -Entropy E-mail: spau0022@tc.umn.edu