10.3 Texture Interface and Basic Textures
Texture is a template class parameterized by the return type of its evaluation function. This design makes it possible to reuse almost all of the code among textures that return different types. pbrt currently uses only Float and Spectrum textures.
The key to Texture’s interface is its evaluation function; it returns a value of the template type T. The only information it has access to in order to evaluate its value is the SurfaceInteraction at the point being shaded. Different textures in this chapter will use different parts of this structure to drive their evaluation.
10.3.1 Constant Texture
ConstantTexture returns the same value no matter where it is evaluated. Because it represents a constant function, it can be accurately reconstructed with any sampling rate and therefore needs no antialiasing. Although this texture is trivial, it is actually quite useful. By providing this class, all parameters to all Materials can be represented as Textures, whether they are spatially varying or not. For example, a red diffuse object will have a ConstantTexture that always returns red as the diffuse color of the material. This way, the shading system always evaluates a texture to get the surface properties at a point, avoiding the need for separate textured and nontextured versions of materials. This material’s implementation is in the files textures/constant.h and textures/constant.cpp.
10.3.2 Scale Texture
We have defined the texture interface in a way that makes it easy to use the output of one texture function when computing another. This is useful since it lets us define generic texture operations using any of the other texture types. The ScaleTexture takes two textures and returns the product of their values when evaluated. It is defined in textures/scale.h and textures/scale.cpp.
The attentive reader may notice that the shared_ptr parameters to the constructor are stored in member variables and wonder if there is a performance issue from this approach along the lines of the one described in Section 9.3 with regard to the Bump() method. In this case we’re fine: Textures are only created at scene creation time, rather than at rendering time for each ray. Therefore, there are no issues with contention at the memory location that stores the reference count for each shared_ptr.
Note that the return types of the two textures can be different; the implementation here just requires that it be possible to multiply their values together. Thus, a Float texture can be used to scale a Spectrum texture.
ScaleTexture ignores antialiasing, leaving it to its two subtextures to antialias themselves but not making an effort to antialias their product. While it is easy to show that the product of two band-limited functions is also band limited, the maximum frequency present in the product may be greater than that of either of the two terms individually. Thus, even if the scale and value textures are perfectly antialiased, the result might not be. Fortunately, the most common use of this texture is to scale another texture by a constant, in which case the other texture’s antialiasing is sufficient.
10.3.3 Mix Textures
The MixTexture class is a more general variation of ScaleTexture. It takes three textures as input: two may be of any single type, and the third must return a floating-point value. The floating-point texture is then used to linearly interpolate between the two other textures. Note that a ConstantTexture could be used for the floating-point value to achieve a uniform blend, or a more complex Texture could be used to blend in a spatially nonuniform way. This texture is defined in textures/mix.h and textures/mix.cpp.
To evaluate the mixture, the three textures are evaluated and the floating-point value is used to linearly interpolate between the two. When the blend amount amt is zero, the first texture’s value is returned, and when it is one the second one’s value is returned. We will generally assume that amt will be between zero and one, but this behavior is not enforced, so extrapolation is possible as well. As with the ScaleTexture, antialiasing is ignored, so the introduction of aliasing here is a possibility.
10.3.4 Bilinear Interpolation
The BilerpTexture class provides bilinear interpolation among four constant values. Values are defined at , , , and in parameter space. The value at a particular position is found by interpolating between them. It is defined in the files textures/bilerp.h and textures/bilerp.cpp.
The interpolated value of the four values at an position can be computed by three linear interpolations. For example, we can first use to interpolate between the values at and and store that in a temporary tmp1. We can then do the same for the and values and store the result in tmp2. Finally, we use to interpolate between tmp1 and tmp2 and obtain the final result. Mathematically, this is
Rather than storing the intermediate values explicitly, we can perform some algebraic rearrangement to give us the same result from an appropriately weighted average of the four corner values: