Tutorials

Sections

j3d.org

Dot3 bump mapping in Java3D

By Joachim Diepstraten

A working demo plus sourcecode can be found here

Before we explain how to implement Dot3 bump mapping in Java3D we first have to address some requirements. The Dot3 bump mapping technique described here only works with the latest Java3D (1.3beta1 and later). Only Java3D 1.3 provides a new texture combine mode to compute the dot product on texel/pixel level. Second your graphic card should support textureCombineDot3 and should have at least one TextureUnitState. You can check both by querying the Canvas3D with

    queryProperties("textureCombineDot3Available");
    queryProperties("textureUnitStateMax");
If both requirements are fullfilled you're ready for Dot3 bump mapping.

The difference between "real" bump mapping and dot3 bump mapping is instead of penetratring the surface normal and binormal at each rendered pixel of a surface in dot3 bump mapping a normal map is used. A normal map is similair to the usual texture map but instead containing color values (as rgb) it contains normal values (as xyz). This map is placed on the surface. You can calculate a normal map from a luminance (gray value) picture by simply treading each pixel in the picture as a vertex of a triangle. Three pixels build up a triangle (x/y, x+1/y, x/y+1). You simply have to calculate the difference of the color values between these three points (you can add an additional scaling factor if you want).

    dx = scaleFactor * (x+1/y - x/y)
    dy = scaleFactor * (x/y+1 - x/y)
    dz = 1.0f
Afterwards you have to normalize your normal, clamp it to the range of [0..1] and save it to the BufferedImage of the ImageComponent2D for your desired texture map.

Okay now we have the normal map what's now still left to do is to tell the Java3D render to calculate the dot product between our new generated normal map and a light vector. This can be achieved by defining a TextureUnitState and a corresponding TextureAttribute. You can use the blending color of a TextureAttribute to set a constant light vector (this will make it of course a directional light source). Don't forget to clamp the values to [0..1]. For example you want to have a directional light with a direction vector (-1, 0, 1) your corresponding blending color would be (0.0f, 0.5f, 1.0f, 0.0f). The fourth value defines the alpha and can be set to zero as it won't have any influence.

To activate the dot3 combiner for texturing you need following lines:

    TextureAttributes texAttr = new TextureAttributes();
    texAttr.setTextureMode(TextureAttributes.COMBINE);
    texAttr.setTextureBlendColor(light.x,light.y,light.z,0.0f);
    texAttr.setCombineRgbMode(TextureAttributes.COMBINE_DOT3);
The last line tells Java3D to use dot3 COMBINE_MODE on the RGB values which means it will calculate the dot product between the given two sources and store the result in r,g,b in the framebuffer. In Java3D you can set three sources for each combine mode but most of the combine modes use only two, so the third one can be set to anything you would like. This means the first source will be our normal map and the second source our constant light vector which has been set as texture blend color
    texAttr.setCombineRgbSource(0,TextureAttributes.COMBINE_TEXTURE_COLOR);
    texAttr.setCombineRgbSource(1,TextureAttributes.COMBINE_CONSTANT_COLOR);
Instead of using as second source COMBINE_CONSTANT_COLOR (shown in Figure 1) you could also set COMBINE_OBJECT_COLOR (shown in Figure 2) this leads to interesting results. Or you could use a second texture as light vector map.


Figure 1: The effects of applying COMBINE_CONSTANT_COLOR

Figure 2: The effects of applying COMBINE_OBJECT_COLOR

Now we have a diffuse dot3 bump mapping. The only drawback with this method we can only have gray value pictures as in each r,g,b component the same value will be written. The only solution to overcome this limitation I've found until now is to use multipass rendering. In the first pass you simply render the object just with the emissive, ambient and difusse material term and with the second pass you add the texture, TextureAttributes and TextureUnitStates with a transparency term. For example:

    TransparencyAttributes transAttr =
      new TransparencyAttributes(TransparencyAttributes.BLENDED,0.5f,
                         TransparencyAttributes.BLEND_ONE_MINUS_img_ALPHA,
                         TransparencyAttributes.BLEND_ONE);
Actually there should be another way by using a second TextureUnitState and a second COMBINE_MODE but somehow I couldn't get it to work on any of the many different hardware and software configuration I tried it on. The same goes for the specular dot term where you use the halfway vector with another dot3 combine and add the result to the first one.