The Aqsis Texturing Library

Overview

The old aqsis texturing code had become stale and difficult to work with, while also having significant quality problems. An initial effort by Chris Foster to rewrite some parts of the code ended up turning into a full-blown rewrite of the entire texturing subsystem.

As part of the rewrite, the texturing code has been split off into a new library, aqsistex. The new library handles a bunch of different texture-related tasks, including high quality texture filtering for antialiasing as well as texture file input and output via an extensible interface.

Changes introduced by aqsistex

Improvements for filtering of plain textures

Texture filtering for plain textures has been vastly improved by incorporating an analytical filter technique known as Elliptical Weighted Averaging (EWA). The new implementation produces images of significantly higher quality than the old, while also having a speed advantage.

The following is an example of some output from the new code with a swirled texture warp mapped onto a bilinear patch. The runtime for the new code is roughly 40% of time for the old texture code in this case after subtracting off the time not spent in texturing.

Input texture Result
Input texture Swirled texture mapped onto a patch

In general, the increase in quality relised in aqsistex is due to several factors:

  • Correct anisotropic filtering
  • Improvements to texture blur
  • Elimination of mipmap level selection bugs
  • Analytical rather than stochastic filtering

Correct anisotropic filtering

Firstly, the new code correctly performs anisotropic filtering. This is necessary whenever the texture transformation stretches the texture much more in one direction than the other.

When filtering a region of any texture, it is desirable to choose from one of several pre-downsampled images. Collectively these downsampled images make up what is known as a mipmap. When using isotropic filtering (as in the old code), there is no correct way to choose a mipmap level; there is nearly always a tradeoff to be made between aliasing and blurring. Isotropic texturing code tends to prefer blurring over aliasing, and the old aqsis texture filtering was no exception. This meant that the old code often produced rather blurry results.

Here's an example where the input texture from above has been stretched a lot more in one direction than the other:

Old code New code

Improvements to texture blur

The old implementation of texture blur had several bugs, and was quite inefficient. Adding some position-dependent blur to the swirled texture warp above gives the following pictures. For this example, the speedup is very dramatic - the new code takes only about 15% of the time taken by the old code overall.

Old code New code
problematic blurring correctly blurred texture

Mipmap level selection bugs

The old code sometimes did a bad job of selecting a relevant mipmap level, resulting in particularly ugly artifacts. An example can be seen in the anisotropic filter image produced with the old code. The horizontal stripes of dramatically different blurryness at the top and middle of the image are a result of this bug.

Analytical filtering

The old code relied on stochastic filtering - that is, it randomly sampled the image inside the filter support region. Stochastic filtering is easy to implement, but unfortunately always leads to some form of noise in the image. The new code avoids this because EWA is an analytical filter over the source texture. That is, filtering with EWA is a convolution between an analytically known filter function and the source texture.

The elimination of noise in the following example images demonstrates the advantages of using analytical filters and direct convolution. Similar noise may be seen in the images demonstrating anisotropic filtering. The images show a simple grid pattern mapped onto a plane. The pixelfilter has been set to a high quality 8×8 sinc filter to remove any blurriness resulting from the geometry antialiasing.

Old code New code
old texturing showing the blurred grid new texturing showing very clear grid features

Shadow filtering improvements

Correct surface approximation during shadow filtering

The old shadow filtering code had some bugs which caused incorrect filtering, especially when using large shadow blurs. The only way to fix these problems was to turn up the shadow bias to unreasonably large values. The new code avoids this by computing the correct linear approximation to the surface at the centre of the filter.

The following examples show the improvements to bias handling which we get from performing a correct linear approximation. Both images are rendered with “depthfilter” “min” and a bias of 0.01. Evidently, the old code shows significant self-shadowing while the new code shows none. The new code remains correct even when the bias is turned down to 0.001.

Old code New code
old sharp shadows and bias new sharp shadows and bias

Here's another example. Note the incorrect shadowing on the bottom of the cylinder facing the light in the following scene. These pictures were rendered with midpoint shadows, which normally should allow the user to neglect the bias altogether. In this example, the old code required the user to turn up the bias significantly to get rid of the extra shadowing.

Old code New code
old midpoint blur problem fixed midpoint blur problem

Sharper shadows for low shadow map resolutions

Previously the shadow filtering implementation could not correctly produce sharp shadows, and exhibited blocky artifacts with low shadow map resolutions. These problems have been fixed in the new implementation.

Correct anisotropic filtering for shadows

The old texture code also filtered images using a texture-aligned rectangular region. This method actually worked surprisingly well, but occasionally led to a preferred orientation in the shadows which were cast, particularly with large blur.

Improved environment map filtering

The previous environment map filtering suffered from more than one serious bug. Firstly, the mipmap filtering incorrectly calculated the mipmap level. This led to excessive blurring in some areas, while other areas were aliased. Secondly, the field of view for cube face environment maps couldn't be expanded to more than 90 degrees. Doing so would cause the sampling code to misalign the cube faces.

In addition to these bugs, the improvements to anisotropic filtering and other aspects of plain texture filtering also apply to environment filtering. This includes a speed increase, which for the example below is approximately a factor of 4 in the filtering code itself.

The example below shows 360 degrees of an environment mapped onto a plane with an angular fisheye lens. The improvements due to removal of the bugs are clear.

Old code New code
old environment mapping new environment mapping

Bug fixes

The new implementation has fixed numerous bugs along the way. These include:

  • Significant unwanted texture blurring is now gone (particularly for anisotropic texture deformations)
  • Mipmap level selection was sometimes incorrect.
  • Arguments to texture() and shadow() are correctly varying (eg, “blur”) or uniform (eg, “fill”), as specified by the RISpec.
  • Notice and complain about strange TIFF formats.
  • textureinfo() works at the file level rather than the texture sampler level, so no longer issues spurious warnings.
  • Shadow maps are now sampled correctly near the edges (particularly relevant for large blur)
  • Shadow map filtering now uses a more accurate approximation to the surface enabling the use of significantly smaller shadow biases.
  • Shadow maps use the number of samples requested, rather than a number decided by the implementation.
  • Requests for non-blurred shadows at low shadow resolutions are now correctly honoured.
  • Texture metadata (eg, transformation matrices) is preserved by the mipmapping process.
  • FIXME (there's more)…

Default textures

Aqsis now has well-defined default textures which are used whenever a texture file cannot be found or used for whatever reason. The textures are hardcoded procedurals, designed with two goals in mind:

  • To make missing textures as obvious as possible
  • Allow easy visualization of texture boundaries/orientation for any missing texture.

Default plain texture

The following picture is render of a bilinear patch with default texture applied. The extent of the default texture is bounded by the light and dark grey areas, and tiled periodically onto the patch.

Default plain texture

Default environment texture

The default environemnt texture tries to visualize directions - it has +x, -x, +y, -y, +z, -z marked for the six axis directions. The orientations of the letters correspond to the orientations required when rendering a cube face environment map. When mapped onto a plane using an angular fisheye lens, the default environment texture looks as follows:

Default environment texture

Availability of texture filtering inside DSOs

Since the new texture code has been designed as a seperate library, it's now possible to load and filter textures from user-written DSO shadeops.

Library documentation

The aqsistex library has the following main components:

  1. File loading and saving functionality
    • A generic image I/O interface with support for arbitrary image metadata
    • A specialized image loading interface for tiled images
  2. Texture buffer handling
    • Classes for holding texture homogeneous channel types
    • Classes for dealing with interleaved images with a heterogeneous pixel channel structure
  3. Texture filtering
    • EWA texture filtering on dense and stochastic supports
    • A texture cache
  4. Texture file processing
    • Mipmap creation

FIXME Generate a class list with doxygen, along with class hierarchys etc. Group these into important sections, and write some documentation for each section including examples.


Personal Tools