```

[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

```