There are many different approaches towards bump mapping that can be used to give your textures depth. In this introduction tutorial to bump mapping, we will use height maps to pre-emboss your texture with bumps. Once your texture is embossed with a height map, the embossed texture can be used like a normal texture map, without requiring any additional processing time during rendering.
Pre-emboss bump mapping is easy to implement, provides fast rendering and gives a very realistic end result. Pre-emboss bump mapping do not take light source positions and parameters into account during real-time rendering, and are therefore, not 100% correct from a lighting perspective. The additional rendering speed that is obtained with this rendering technique however, more than compensates for the compromise on technical correctness.
To apply emboss bump mapping to a texture we need two images, a texture bitmap and a height map. The texture bitmap defines the RGBA colors of the texture and the height map specifies the height of each pixel in the texture. The images below respectively illustrates a possible texture bitmap and height map that can be used for an earth model.
On the height map, white colors indicate high positions and black indicates low positions. E.g. Mount Everest is represented by a white color, while the sea level is represented by a black color.
IMPORTANT: It is best practice is to ensure that your height map has the exact same dimensions (e.g. width and height) as the texture bitmap.
To emboss our texture bitmap with a given height map is very straightforward. We simply scan the bitmap pixels from left-to-right, top-to-bottom. At each pixel, we calculate its new color using by using the height map pixel colors directly to the right and bottom of the current pixel coordinate. To illustrate the algorithm, imagine our texture bitmap and height map is the 5x5 pixel bitmaps as shown below.
Using the above images as reference, our emboss algorithm starts at pixel t0 and reads the pixel color values in the height map at (x,y) and (x+1,y+1), that would be pixels h0 and h6 respectively.
The color of pixel t0 is now adjusted with the following formula:
Let's inspect the above pseudo algorithm more closely. t0, h0 and h6 can be any value between 0 (black) and 1 (white). (h0 - h6) calculates the difference in height between the two pixels in the height map. This difference is then multiplied by 2 to give it a larger weight. We now simply add the weighted height difference to the texture bitmap's pixel. The "if" statements clamps our new pixel color value between 0 and 1.
The process above is repeated for each pixel in the texture bitmap. When we reach a border, we simple wrap pixels (e.g. for t4, the corresponding height map pixels to use are h4 and h5). The final embossed texture bitmap is shown below.
The simple pseudo algorithm described above is an easy and fast way to apply bump mapping to textures, by using height maps and an embossing technique. When working with textures and height maps, the separate colors, red, green and blue, need to be taken into account when calculating new pixel color values.
Dim model As R3Model ' temporary model object Dim light As R3LightSource ' light source Dim cam As R3Camera ' camera used in our scene me.Maximize ' maximize the window me.MouseCursor = REALbasic.System.Cursors.StandardPointer ' set mouse cursor ' instantiate the Scene object Scene = new R3Scene Scene.BackgroundColor.SetValue(0, 0, 0) ' set background color ' load textures used in scene Scene.Texture.Append new R3Texture(imgEarthText, imgEarthBump) ' earth texture with bump map Scene.Texture.Append new R3Texture(imgEarthText) ' earth texture with bump map ' add sun light = new R3LightSource light.Position.SetValue(-10, 0, 3) light.Ambient.SetValue(0, 0, 0, 1) light.Diffuse.SetValue(1, 1, 1, 1) light.Specular.SetValue(1, 1, 1, 1) Scene.LightSource.Append light ' add camera cam = new R3Camera cam.DollyOut 3 ' move back so that we can get the model in our view Scene.Camera.Append cam Scene.ActiveCamera = 0 ' set the index of the active camera ' instantiate earth model model = R3_Tut00018Earth model.Scale.SetValue 1, 0.9, 1 model.Rotation.Rotate(20, 0, 0) model.Rotation.Rotate(0, -95, 0) Scene.AppendModel model OpenGLSurface1.Render ' refresh OpenGL surface16. Add the following code to the Paint event of Window1:
OpenGLSurface1.Render ' refresh the OpenGL surface17. Add the following code to the Close event of Window1:
Scene = nil ' remove the scene object from memory18. Add the following code to the Open event of OpenGLSurface1:
R3_OGLInitialize ' initalize OpenGL environment19. Add the following code to the Render event of OpenGLSurface1:
' is the scene object instantiated? if Scene <> nil then ' render the modal normally R3_OGLRenderScene Scene end if20. Add the following code to the Resized event of OpenGLSurface1:
' set the viewport rectangle OpenGL.glViewport 0, 0, OpenGLSurface1.Width, OpenGLSurface1.Height ' set up the perspective projection settings OpenGL.glMatrixMode OpenGL.GL_PROJECTION OpenGL.glLoadIdentity OpenGL.gluPerspective 60.0, OpenGLSurface1.Width / OpenGLSurface1.Height, 1, 100.0 ' select and reset the modelview matrix OpenGL.glMatrixMode OpenGL.GL_MODELVIEW OpenGL.glLoadIdentity21. Add the following code to the MouseDown event of OpenGLSurface1:
Dim model As R3Model ' select model at mouse coordinates model = R3_OGLPickModel(scene, x, OpenGLSurface1.Height - y) ' did the user click on a model? if model <> nil then ' yes, toggle bump map texture on/off if model.Polygon(0).TextureIndex = 0 then model.SetTextureIndex 1 ' flat earth texture else model.SetTextureIndex 0 ' bump mapped earth texture end if OpenGLSurface1.Render ' refresh the OpenGL surface else ' no, so start the rotation action of the earth MousePrevX = x MousePrevY = y return true ' only start the mouse drag operation if the user did not select a model end if22. Add the following code to the MouseDrag event of OpenGLSurface1:
Dim model As R3Model ' temporary model object model = Scene.Model(0) ' get the first model in the scene ' apply a pitch and yaw rotation using the mouse x and y movement model.Rotation.RotateProjection((y - MousePrevY), (x - MousePrevX)) OpenGLSurface1.Render ' refresh the OpenGL surface ' save the mouse position MousePrevX = x MousePrevY = y23. Save and run the project.
On line 15 in the Window1.Open event you can see how a height map is applied to a texture image. When you instantiate a new texture, you simply pass the bump map image as the second parameter to the R3Texture constructor. Note that the colors of height maps are ignored, so make sure your height map is saved as a grey scale image.
On line 16 we instantiate another texture, but this time without a height map.
To get a better understanding of how the pre-emboss bump mapping algorithm is implemented, have a look at the R3Texture.LoadTextureWithHeightMap method.
Now it's time to have some fun. Edit the height map with your favorite 2D editor, and see what happens. E.g. write your name at the center of the height map in a white color and run the project again.