Real-time 3D Bumpmapping
by (13 January 1999)

Return to The Archives

I have seen quite a lot of different ideas about bumpmapping on various pages and in various engines. There's probably no 'best way' to do it, but because the way Focus does its lighting (with the volumetric texture projection) it was kinda complicated, and thus I think I should describe the used algorithm. After all, maybe someone looks at my sources to see how I did the bumpmapping, only to find out that my method would never work for his engine...

On a side note, there is some stuff about bump mapping on Tom Hammersley's page. One of the solutions was sent in by me, and oddly enough Tom thinks it's not a very good way to do it, and states so clearly on his page. A mail from me about this remained unanswered, and thus there will always be a nice bit of (dumb) criticism on his page about me. Thanks man. I'll think twice in the future before I send anything to you.

OK, on to the relevant stuff. :)

Bump Mapping

Basically, what a bump mapping algorithm should do is:
  • a) Disturbing the intensity of light on a screen pixel
  • b) Disturbing the U/V's used for fetching texels.

    Most of the time, b) is ignored, and only the light intensity is altered. I tried to do b) too, but it looks disgusting. I'll talk some more about that later.

    So, I went for the light intensity manipulation. Naturally, the bumps should influence the light intensity. Or, to be more precise: Changes in height of the bump map should cause disturbances, because the slopes on the bumpmap do not face the light source the same way as a plain polygon would do. Steep slopes should be darker, shallow (?correct English?) slopes should receive full brightness.

    An easy way to simulate this is by using the 'slope factor' as a displacement in the lightmap. Because usually the lightmap goes from dark (at the edges) to bright (at the centre), a displacement will cause the desired intensity alteration. The 'slope factor' can be determined by comparing two adjacent texels. I did this for both adjacency in the vertical direction (for V displacement) and in the horizontal direction (for U displacement). I assumed that the maximal difference between two bump-texels is '7', so the slopes range from -7..7 (horizontally) and also from -7..7 vertically. As you may notice, these ranges fit nicely in a single byte; the horizontal slope is represented by the right nibble, the vertical slope by the left nibble. A byte can thus represent 256 different modifications to the local normal vector of a plane. That's good enough.

    I promised to get back on the texture displacement: Because the displacement is measured in integers, texture displacement looks UGLY. Assume that you have a nice sine-slope. The slope will gradually get steeper, and then shallower. Thus, the 'slope value' will go from 1 to 2, to 3, and so on. But, '1' will cause a displacement of 1 texel. '2' will cause a displacement of two texels, meaning that one texel is skipped. It gets worse on the other side of the hill: At first, three texels are skipped. Then two texels are skipped, meaning that the SAME texel is fetched again. This disturbes the texture in an unacceptable way. The only way to prevent this is to use higher precision bumps. That would be too slow in my implementation, so I skipped it.

    The bumpmap is attached to a texture. Because the light is altered by the bumpmap, there should be a direct connection between the texture and the lightsource to get bumpmapping. In Focus, there is no such connection, because lighting is performed in an extra pass.

    I solved this by using a 'bump buffer', wich is filled by the rasterizers. So, whether a pixel is lit or not, a bump map byte is written to the buffer. Since a bump byte is written from the source bumpmap at the same offset as the texels, this is hardly a waste, by the way.

    During the lighting pass, these bytes can then be used as offsets in the lightmaps.
    The first four bits are added to the interpolated 'V', the other four bits to the 'U'.

  • Closing

    The current implementation is not perfect yet. As you may notice, the orientation of the plane does not influence the intensity of the bumps. This should of course be fixed. A good way to do this is to calculate a small look-up table at run time for each bump-mapped polygon. Ideally, this should be an 16x16 table, so that the byte value that is fetched from the bumpmap can be used as a direct index in this table.

    The table contains a translation for each of the 256 possible 'normal vectors' to a displacement. Thus, when the direction of the light source to the plane somehow influences this data, we have convincing bump mapping: Now both the slopes on the bumpmap and the direction of the light source influence the brightness of individual bumps, with a minimal amount of work.

    I will describe how to fill the table at a later stage, once I've implemented it myself.

    Jacco Bikker, a.k.a. "THE PHANTOM"


    Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
    Please read our Terms, Conditions, and Privacy information.