Procedural texturing
(Work in progress)
Procedural texturing is a set of techniques where the color of a pixel is not determined by a texture image but fully or partially by evaluating a mathematical function. This article describes why procedural texturing is useful, what it can and can't do and how it is used and configured in Flightgear.
The terrain texturing challenge
The Flightgear terrain is segmented into different landclasses (Urban, Ocean, Glacier, ShrubCover,...) which describe what the terrain at a location is like. In the default texturing scheme, the file materials.xml is used to describe (among other things) what texture sheet is mapped onto a given landclass.
A typical terrain texture sheet has 512x512 pixels and is mapped onto a 2000m x 2000m sized area, which means that each pixel covers a 4m x 4m area in unsloped terrain. For the sake of the argument, assume the texture has a size of 300 kB in compressed form which expands to about 1 MB in the GPU memory after mipmapping.
The optimal distance to view the texture is when every texture pixel corresponds to a pixel on the screen, as then the maximum possible detail of the texture is exploited. Assuming a 60 deg field of view looking straight down onto the texture, that distance is about 2500 m.
If the texture is seem from a much larger distance, it does not large enough to cover the terrain, and hence it is automatically repeated. From about 3 times the optimal distance, this repetition creates a visually rather pronounced pattern which is referred to as tiling:
If the texture is seen from a much smaller distance, a single texture pixel covers multiple screen pixels, which makes the terrain look coarse-grained and unrealistic:
A third problem occurs because the boundaries of the landclasses are vector data, i.e. they appear as unnaturally sharp division lines between different textures.
Thus, the main challenges for realistic terrain texturing can be summarized as the three areas
- tiling
- resolution
- boundaries
Addressing the challenges
Tiling and resolution are correlated problems, i.e. many solution to one tend to make the other worse. For instance, resolution can easily be dealt with by mapping the texture sheet onto a smaller area, say 500m x 500 m, which gives a pixel size of 1m. However, then tiling becomes apparent from just 1500 m distance, i.e. almost inevitably.
Tiling is also reduced if the variation of contrast inside a texture is reduced, i.e. the texture becomes more 'dull' and the pattern repetition is less obvious:
However, contrasts within a texture are the best way to distract the eye from the sharp landclass boundaries, so if one uses low contrast textures, the landclass boundary problem gets often worse.
Tiling can also be reduced at the expense of texture detail if a single texture sheet is mapped onto a larger area.
To some degree, all problems lessen with increased texture resolution - using 4096x4096 texture sheets allow 0.5m x 0.5m resolution if mapped onto the same areas - but memory consumption quickly becomes as issue as a single texture sheet of that size uses ~64 MB of graphic memory already.
Photo texturing
The tiling problem can be solved if each 2000m x 2000m area to be textured does not get the same texture sheet. If aerial imagery is used (see Photoscenery) then also the landclass boundary is solved. However, this inevitably creates a resolution problem. To see this, consider the following numbers: On a clear day, the visibility from airliner cruise altitude can easily be 120 km. This means that 45.000 square kilometers of terrain are visible at any given time. Using 2000mx2000m mapped texture sheets as in the example above, unique terrain texturing then needs 11.000 different texture sheets with a size of 1 MB each in graphic memory, or about 11 GB of graphic memory, quite exceeding the capabilities of modern graphic cards. Assuming that about 1 GB of graphic memory is available for terrain textures (clouds, models, the cockpit,... also need memory), the individual pixel must have a size of 12 m x 12 m or larger, which means that photoscenery can either not be used for large visibility, or has a terrible resolution from close-up, or uses a LOD scheme (the above photoscenery would still use 3 GB of harddisk space though).
Photo-texturing has other disadvantages - for instance the photographs are usually taken in a particular season in certain light, so the scene may easily seem unrealistic or wrong in a different season or at a different time of the day. Flightgear texturing also encodes meta-information (where on a texture to place trees or buildings) - such meta-information can not easily be encoded in photoscenery.
Procedural texturing
Procedural texturing is a solution to both the tiling and the resolution problem, and partially also for the boundary problem. A mathematical function, unlike a lookup table (which a texture in essence is) does not end at any boundary, i.e. it can simply be used across the whole scene without ever creating a repetition. Using a suitable noise function to mix various textures, a complete de-tiling effect can be achieved (this is really the same scene):
A function can also be evaluated at any scale resolution. Unlike a texture which needs to be stored regardless if it is used or not, a function only needs to be evaluated for every screen pixel, i.e. the workload and memory requirement do not grow quadratically with the needed resolution but only linear. This scaling allows to dynamically add details to the scene as one gets closer (this is really the same scene):