Some renderers provide support for texture mapping, including color, normal, and bump maps; whether or not they do can be queried via the methods hasColorMapping(), hasNormalMapping(), and hasBumpMapping(). If supported, such mappings may be set up and queried using the methods
The props argument to the set methods either contains the properties required to set up the mapping, or, if null, disables the mapping. When enabled, color, normal and bump maps will be applied to subsequent draw operations involving triangle primitives for which texture coordinates have been assigned to the vertices.
At present, texture coordinates can be defined for primitives using either draw mode (Section 2.3.4), or by creating a RenderObject (Sections 2.4 and 2.4.6). Texture coordinates are assigned using the OpenGL convention whereby and correspond to the lower left and upper right of the image, respectively.
Normal and bump mapping will not work if shading is set to Shading.NONE or Shading.FLAT. That’s because both of those shading modes restrict the use of normals when computing primitive lighting.
Renderers based on OpenGL 3 support color, normal and bump mapping. Those based on OpenGL 2 support only color mapping.
The properties specified by ColorMapProps, NormalMapProps, or BumpMapProps contains the source data for the mapping along with information about how to map that data onto drawn primitives given their texture coordinates. These properties include:
A boolean specifying whether or not the mapping is enabled;
A string giving the name of the texture source file. This can be any image file in the format supported by the standrad package javax.imageio, which includes JPEG, PNG, BMP, and GIF;
An instance of TextureMapProps.TextureWrapping specifying the wrapping of the s texture coordinate;
An instance of TextureMapProps.TextureWrapping specifying the wrapping of the t texture coordinate;
An instance of TextureMapProps.TextureFilter specifing the minifying filter;
An instance of TextureMapProps.TextureFilter specifing the magnifying filter;
The color to be used when either sWrapping or tWrapping is set to TextureWrapping.CLAMP_TO_BORDER;
For ColorMapProps only, an instance of Renderer.ColorMixing that specifies how the color map is combined with the nominal coloring of the underlying primitive, which is in turn determined by the current diffuse/ambient color and any vertex coloring that may be present (Section 2.3.7). The default value for this is MODULATE, implying that the color map is modulated by the nominal coloring. Not all renderers support all mixing modes; whether or not a particular color mixing is supported can be queried using
For ColorMapProps only, a boolean that specifies whether the color map should respond to diffuse/ambient lighting;
For ColorMapProps only, a boolean that specifies whether the color map should respond to specular lighting;
For NormalMapProps and BumpMapProps only, a float giving a scaling factor for either the x-y components of the normal map, or the depth of the bump map.
TextureMapProps.TextureWrapping is an enum that describes how texture coordinates outside the canonical range of are handled. There are four available methods, which correspond to those available in OpenGL:
Method | Description | OpenGL equivalent |
---|---|---|
REPEAT | pattern is repeated | GL_REPEAT |
MIRRORED_REPEAT | pattern is repeated with mirroring | GL_MIRRORED_REPEAT |
CLAMP_TO_EDGE | coordinates are clamped to | GL_CLAMP_TO_EDGE |
CLAMP_TO_BORDER | out of range coordinates are set to a border color | GL_CLAMP_TO_BORDER |
REPEAT is implemented by setting the integer part of the coordinate to 0. For MIRRORED_REPEAT, mirroring is applied when the integer part is odd. See Figure 2.26.
TextureMapProps.TextureFilter is an enum that describes the filtering that is applied when the source image needs to be either magnified or minified. Specifically, for a given pixel being textured, we use the filter to compute a texture value from the texels in the texture image. There are six filter types, corresponding to those available in OpenGL:
Method | OpenGL equivalent |
---|---|
NEAREST | GL_NEAREST |
LINEAR | GL_LINEAR |
NEAREST_MIPMAP_NEAREST | GL_NEAREST_MIPMAP_NEAREST |
LINEAR_MIPMAP_NEAREST | GL_LINEAR_MIPMAP_NEAREST |
NEAREST_MIPMAP_LINEAR | GL_NEAREST_MIPMAP_LINEAR |
LINEAR_MIPMAP_LINEAR | GL_LINEAR_MIPMAP_LINEAR |
NEAREST uses the texel nearest to the pixel center, while LINEAR uses a weighted average of the four texels nearest to the pixel center. The remaing four MIPMAP values perform the filtering with the aid of mipmaps, which are images of diminishing size used to accomodate lower resolution rendering of the primitive. The OpenGL documentation should be consulted for details. Mipmaps are generated automatically if one of the MIPMAP values is selected.
Color, normal, and bump maps can set up independently or combined with each other. Listing 10 gives a complete example, showing all three maps applied to a simple planar rectangle to make it look like a shiny brass plate embossed with an Egyptian friz pattern. A color map adds character to the brass appearance, a normal map adds a ”crinkled” effect, and a bump map adds the friz pattern.
Properties for the mappings are created by the method createMaps(), using the raw images shown in Figure 2.27, and stored in the member variables myColorMap, myNormalMap, and myBumpMap. It uses a method getDataFolder(), not shown, which returns the path to the folder containing the image files. Whether or not specific mappings are enabled is controlled by the member variables myColorMapEnabled, myNormalMapEnabled, and myBumpMapEnabled.
The render() method does the actual rendering. It begins by increasing the shininess (10 being shininer that the default of 32), and setting the base and specular colors. Setting a separate specular color is necessary for creating specular effects that stand out from the base color. Mappings are then set if enabled, and the renderer’s draw mode is then used to draw the plate using two triangles with texture coordinates assigned to the vertices. Figure 2.28 shows the rendered plate with different mapping combinations applied.
The example described in Section 2.5.2 can also be implemented using a render object. The modification involves adding code to create the render object, and then using it to perform the draw operation in the render() method:
The method createPlateRenderObject() creates a render object for the plate, using the same vertex and texture coordinate definitions found in Listing 10. prerender() then uses this method to creates the render object once, on demand. Because the render object in this example is fixed, it is not actually necessary to create it inside prerender(), but we do so because this is where it is recommended that render objects be maintained, particularly if they are being continuously updated with application data.
For an example of this mapping being implemented directly using a PolygonalMesh object and RenderProps, see Section 2.6.