Prev: Overview Next: A Simple Texture

2. Textures and TextureSpecs

A Texture in Art of Illusion is a Java class which subclasses artofillusion.Texture. In all cases that you will write, you will not directly subclass Texture, but instead subclass either Texture2D or Texture3D.

Note that a Texture class is not the same as a Texture object. For example, Art of Illusion includes a class called ImagedMappedTexture, which implements the standard image based textures. An individual object of this class specifies values for many different parameters (the images, colors, and values used to define the texture). Similarly, your own Texture classes may include free parameters which affect the appearance of the texture. A user may then create many different instances of your class, each with its own values for these parameters.

The job of a Texture class is to describe the surface properties of an object. It does this by providing a method called getTextureSpec() which takes the coordinates of a point on the surface, and calculates the surface properties at that point. (There are several other methods which the Texture class must also provide. These are described in detail in chapter 6.) When a Renderer is generating an image, it repeatedly calls getTextureSpec() to determine the properties of various points on a surface, then uses those properties to decide how to color it.

The coordinates which are passed to getTextureSpec() refer to points in texture space. This is the 2D or 3D space (depending on whether you subclass Texture2D or Texture3D) in which the Texture is defined. When a user assigns your texture to an object, they can choose from a variety of mappings which define the correspondence between points in texture space and points on the surface of the object.

The surface properties are returned in a TextureSpec object. Here is the definition of the TextureSpec class:

public class TextureSpec
{
  public final RGBColor diffuse, specular, transparent, emissive;
  public double roughness, cloudiness;
  public final Vec3 bumpGrad;

  public TextureSpec();
}
The diffuse, specular, and transparent colors act as filters on the light striking the surface. For example, the light transmitted by a surface is equal to (transparent color) x (incident light). The diffuse and specular colors are similarly used to calculate the amount of light diffusely and specularly reflected by the surface.

The emissive color gives the amount of light emitted by the surface. After a Renderer calculates the diffusely reflected light, it adds the emissive color to it. Thus, this is the color of the surface when it has no light shining on it. (Remember that the ambient light in a scene contributes to the diffusely reflected light, and hence also contributes to the color of a surface even when there are no explicit lights shining on it. You should only use a non-zero emissive color when you actually want the surface to glow.)

The roughness parameter is a value between 0 and 1 which describes how shiny the surface is. This is used for two purposes: determining the size of specular highlights, and (for Renderers that support glossy reflections) determining how sharp or blurry reflections in this surface will be. Similarly, cloudiness determines how sharp or blurry objects will appear when viewed through this surface.

The bumpGrad parameter is the gradient of the bump height function. This is relatively straightforward for 2D textures: the bump height is defined as a function in the 2D texture space. The x and y components of bumpGrad specify the gradient of this 2D function, and the z component is ignored.

For 3D textures, on the other hand, it can be a little confusing at first, since the bump height is only meaningful on the surface of an object. Nonetheless, it is defined as a continuous function in three dimensions. The value of this function at a point on the surface determines the bump height at that point. What you need to return is the (full 3D) gradient of this function.

(Note that if your texture uses bump mapping, it also must override the bumpMapped() method to return true. Otherwise, the value of bumpGrad is simply ignored.)

If you are used to the standard Texture classes in Art of Illusion (uniform textures, image mapped textures, etc.) this list of properties should look quite familiar. Be careful: appearances can be deceiving. You may notice, for example, that transparency and specularity are missing. Furthermore, the diffuse, specular, and transparent colors have slightly different interpretations from what you are used to.

The standard texture classes calculate the diffuse, specular, and transparent parameters using the following formulas:

diffuse = diffuseColor * (1-transparency) * (1-specularity)
specular = specularColor * (1-transparency) * specularity
transparent = transparentColor * transparency

This allows the user to change the overall transparency or specularity of a texture by moving a single slider, which is very convenient from a user interface point of view. The underlying API is slightly more general, however. For example, by using the standard Texture classes there is no way to create a texture which reflects 100% of the red light incident on it, and transmits 100% of the green light. In your own Texture classes you can easily do this: simply set diffuse to (1, 0, 0) and transparent to (0, 1, 0). You can even create "nonphysical" textures in which the total reflected and transmitted light is greater than the amount of light that was originally incident on the surface, by having correponding color components of diffuse, specular, and transparent add up to more than 1. This is not realistic, of course, but it can be a useful artistic effect.

You also may notice that there is no parameter for specifying the displacement height. There is a separate function for this purpose, getDisplacement(). That is because displacement mapping is often done in a preprocessing stage, separately from the shading calculations for which the other texture parameters are used.

Here is the complete signature of the getTextureSpec() method in the Texture2D class:

public void getTextureSpec(TextureSpec spec, double x, double y, double xsize, double ysize, double t, float param[])
In Texture3D, it has two additional parameters:
public void getTextureSpec(TextureSpec spec, double x, double y, double z, double xsize, double ysize, double zsize, double t, float param[])
The surface properties are returned in spec. x, y, and z give the coordinates in texture space for which the properties need to be calculated. xsize, ysize, and zsize are used for antialiasing, which is discussed in chapter 5. The actual properties which you should return are not those for the point (x, y, z), but rather the average properties for a region of width (xsize, ysize, zsize) which is centered at the point (x, y, z). t is the time (in seconds) for which the properties are being evaluated. This allows you to create animated textures. (At present, Art of Illusion has no support for animation, so t will always be equal to 0. Needless to say, that will be changing at some point in the future.)

Finally, every texture may define any number of per-vertex texture parameters. When a user assigns your texture to a vertex based object (such as a triangle mesh or spline mesh), they can specify the value for each parameter at every vertex. When getTextureSpec() is called, the parameter values for the given point on the surface are given in the param array. You can use them in whatever way you want in calculating the surface properties. This is a very powerful mechanism for allowing the texture to vary over the surface of an object in a user defined way.

Prev: Overview Next: A Simple Texture