Chapter 4
Arrays, Tables and Textures

Textures are encapsulated in opaque objects allocated by the system rather than by number or texture unit number. This permits the specification of textures separately from the allocation of texture units. The runtime system will bind textures on demand to the appropriate texture units when needed by a particular shader.

Textures are supported in Sh through the use of ShArray , ShTable , and ShTexture objects. Use of these objects permits the use of the “[]” and “()” operators to perform lookups in shader programs and provides a unified interface to textures (so the same interface works for all target platforms).

The ShArray types support nearest-neighbour lookup, the ShTable types support linear interpolation but without filtering, and the basic ShTexture types support both linear interpolation and filtering.

The “[]” lookup operator places texels at integer coordinates but still performs interpolation between them (on the ShTable and ShTexture types only). The “()” operator scales the lookup so the lower-left texel is at (0,0) and the upper-right texel is at (1,1), as usual in OpenGL. The “[]” operator can also be used on the left hand side of an expression (outside a shader definition) to update a particular texel of a shader. The update is actually done to a copy of the texture stored in the host’s memory; updates to the version of the texture stored in the video card is done lazily.

The basic texture object type is the ShArray type, with different interpolation and filtering modes supported with template-based type modifiers.

If template typedefs were allowed in C++, we might define the ShTexture2D type, which supports both bilinear interpolation and MIP-mapped filtering, as follows:


template <typename T>
typedef ShMIPFilter< ShInterp<1, ShArray2D<T> > > ShTexture2D<T>;

Type modifiers are smart enough to check if the underlying hardware texture can support interpolation or filtering with a simple mode change. If so, that mode is simply enabled when the texture is used. Otherwise, shader code is generated. These modifiers also avoid repeatedly generating code (applying ShMIPFilter twice, is no different than applying it once) by seeing if a given mode is already turned on when it is applied.

Modes are indicated with type modifiers like this rather than publically accessible run-time switches to avoid having to recompile shaders implicitly. Unfortunately, certain filtering and interpolation modes might require the insertion of shader code as well as particular settings in the underlying system texture object, and shaders might also depend on the particular interpolation modes implemented by a texture. For instance, suppose we wanted to support cubic interpolation. We might want to build upon linear interpolation supported by the hardware texture. Another example of this is simple linear interpolation, which on the GeForceFX (for instance) is supported for byte textures but not floating point textures. Sh is designed to hide these dependencies from the user by inserting shader code as needed. Because Sh inserts this code only upon compilation to a particular target, preserving the original code, the user can recompile an existing program for different targets (e.g. for cross-compilation or to switch between a GPU and CPU implementation at runtime) without having to redefine the program.


Note: This manual is available as a bound book from AK Peters, including better formatting, in-depth examples, and about 200 pages not available on-line.