Terrain Texture Blending on a Programmable GPU

 

Tim Jenks

tim at jenkz dot org

http://www.jenkz.org/

September 2005

 

Abstract

 

Blended textures are often applied to three dimensional terrain meshes to create realistic looking terrain for computer graphics applications, notably computer games. Many existing techniques were designed around multi-pass rendering that does not take advantage of modern day programmable GPU architectures. We propose a simple way to implement real time texture composition on a programmable GPU to produce unique textures for use in terrain rendering.

 

Introduction

 

Texturing in computer graphics is used to add detail, colour and effects to otherwise plain geometry. Textures are applied to three-dimensional terrain meshes to create the illusion of a rich landscape – grassy plains, sandy shores, snowy mountain peaks and rocky cliff faces.

 

The first terrain texturing techniques used 2D tiles – each land type had it’s own set of tiles, for grass, sand, mud, snow and these tiles were textured per-quad at relevant positions on the terrain mesh. Harsh seams between different tiles are visible; the solution was to create a set of blending tiles to use between areas of adjoining texture types.

 

Later and more effective techniques, such as that outlined by Bloom, C. (2000), blend textures using hardware multi-texturing to create seamless and unique blending between different textures applied to the terrain mesh.

 

Many modern techniques use multi-pass rendering to achieve their desired effects, which is not a suitable technique for modern programmable GPU architectures. This article extends upon the article by Glasser, N. (2005) and describes a simple and fast way of implementing terrain texture blending on modern programmable graphics hardware.

 

Related Works

 

Bloom, C. (2000) Terrain Texture Compositing by Blending in the Frame-Buffer
 

Bloom, C. (2000) outlines the first publication of terrain texture blending which has been the groundwork of the majority of terrain texture blending since. Bloom, C. (2000) describes the basics of alpha blending overlaying textures, known in the article and to many since as “Splats”, using the fixed function pipeline and multiple passes on hardware that cannot support multi-texturing.

 

Bloom, C. (2000) outlines a detailed method of generating alpha values for the texture splats considering neighbouring textures, heights, slopes and normalization of the alpha values to produce better results.

 

Whilst the work is the foundation of much of this article and articles and publications since, the work carried out is less applicable to modern graphics hardware as it was produced before programmable graphics hardware was available.

 

Corpes, G. (2001) Procedural Landscapes

 

Corpes, G. (2001) reviews some history of terrain rendering and texturing from the early 1990s and explains a brief introduction to terrain “prototexture” blending.

 

Luna, F.D. (2003) Multi-texturing in a Pixel Shader

 

Luna, F.D. (2003) describes how to implement basic multi-texturing in a pixel shader using HLSL. Multi-texturing and texture combination are the basis for terrain texture blending and therefore this introduction to multi-texturing using HLSL is an important step to implementing terrain texture blending on programmable GPUs.

 

Glasser, N. (2005) Texture Splatting in Direct3D

 

Glasser, N. (2005) explains Bloom, C. (2000) texture splatting in more detail alongside an implementation description using both the fixed function pipeline and an assembly pixel shader

 

Introduction to Terrain Texturing Blending

 

Basics of Terrain Texture Blending

 

Terrain texture blending is achieved by overlaying different textures on top of each other depending on the height of the terrain mesh, or another attribute such as the gradient of the terrain at that particular point. This overlaying is achieved using alpha blending.

 

A large terrain mesh is usually split into chunks when dealing with rendering, these chunks vary in size but we will assume a 17x17 vertex regular grid. For each texture that will be applied to the terrain chunk in question an associated alpha map is created either procedurally by the engine or manually by an artist using a tool. This alpha map indicates what areas of the terrain chunk the texture should apply to.

 

A base texture is first applied over the whole of the chunk, with further textures blended on top of the base texture depending on their alpha map. Figure 1 shows this texture combination.

 

  *    =

Figure 1: Texture Blending using Alpha Maps. Textures from Glasser, N. (2005) and Nvidia.

 

Alpha Maps

 

For each terrain texture an alpha maps matching the size of the terrain chunk must be created. A unique alpha map texture can be generated and stored separately for each terrain chunk, however a more efficient method is to store the alpha values for four maps in a single RGBA texture – one channel per alpha map.

 

This technique allows four alpha maps to be stored in a single texture, saving texture memory.

 

  

Figure 2: Combining two alpha maps into one RGB texture. Textures from Glasser, N. (2005)

 

Figure 2 shows three separate alpha maps and their combination in the RGB channels. Programmable GPUs allows a pixel shader to extract the individual alpha maps from the combined texture separately by reading from the four channel intensities separately.

 

Procedural Alpha Map Generation

 

Exactly how the alpha map is generated is left up to the reader, as the method chosen varies between application and what final terrain appearance is required.

 

A basic procedural method is to separate the terrain chunk into four sections by height, allowing for four different textures to be placed starting with low-lying land through to mountain peaks.

 

Minimum, maximum and average terrain heights for the chunk can be used to separate the terrain into these sections, then for each texture upper and lower bounds can be declared. If a vertexes height falls within a particular textures bounds then it’s alpha value can be set in the alpha map texture. Figure 3 outlines basic psudeo-code for this approach:

 

for each vertex in the terrain chunk:

      if height of the terrain mesh applies to texture 1:

            set the red channel intensity to desired alpha

      elif height of the terrain mesh applies to texture 2:

            set the blue channel intensity to desired alpha

      elif height of the terrain mesh applies to texture 3:

            set the green channel intensity to desired alpha

      elif height of the terrain mesh applies to texture 4:

            set the alpha channel intensity to desired alpha

 

Figure 3: pseudo code for procedural height based alpha map generation

 

The alpha value indicates the intensity of the texture coverage at that point on the chunk – High alpha values mean the texture will overlay all lower textures in that area, low alpha values mean the texture will barely be visible, if at all.

 

Using the upper and lower bounds example above, if the height of the vertex falls in the middle of a textures height influence, it could have a high alpha value set making the texture prominent. If the height falls nearer to the bounds then a lower alpha value is set.

 

Manual Alpha Map Generation

 

In the computer gaming industry it is important to leave a large proportion of influence with the artists. An artist in a world-editor tool could generate the alpha map for a texture to explicitly design where the texture would be visible. Such a world-editing tool would allow the artist to interactively change the alpha map of a particular texture and see the changes in real time. The alpha maps can then be exported and then loaded into the rendering application at terrain chunk load or page time.

 

Alternatively, another manual approach could be to edit the alpha maps in imaging editing software. Alpha maps created using this approach would be pre-generated and stored on disk to later be loaded by the.

 

These approaches however are not realistic for very large terrain visualization due to the number of static alpha maps that would need to be produced. For very large terrains a procedurally generated alpha map is a better choice.

 

Implementing on a programmable GPU

 

Terrain Texture Blending in Hardware

 

It is possible to implement the above techniques in hardware using the DirectX fixed function pipeline without the need of a programmable GPU. However, the fixed function pipeline does not allow for the explicit reading of individual texture colour channels in hardware, therefore combined alpha maps would not be possible using the fixed function pipeline and each terrain texture would require it’s own alpha map in texture memory.

 

Programmable GPUs using a pixel shader allows for the reading of individual texture colour channels, allowing for a combined alpha map and saved texture memory.

 

To achieve the desired result using the fixed function pipeline the D3DBLEND_INVSRCALPHA texture operation must be performed. This operation performs the following:

 

ResultantColor = Alpha * Texture + (1 – Alpha) * PreviousColor

 

This operation is what must be implemented on the programmable GPU.

 

For both HLSL and assembly implementations the general approach is as follows:

 

  1. Procedurally generate a set of alpha maps for the desired terrain textures (or load pre-generated alpha maps from disk).
  2. Load the set of terrain textures that will be applied into memory.
  3. Initialise the pixel shader.
  4. Set the terrain texture stages for the pixel shader to reference the alpha map(s) and terrain textures.
  5. Render the geometry using the pixel shader.

 

High Level Shader Language Implementation

 

In this implementation five textures are used to blend four terrain textures using the HLSL. The five textures are:

 

  1. Combined Alpha Map (R= Texture3 Alpha, G=Texture4 Alpha, B=Texture 5 Alpha, A=255)
  2. The base texture applied to the whole of the terrain chunk
  3. First layered texture (alpha map stored in the red channel of Texture 1)
  4. Second layered texture (alpha map stored in the green channel of Texture 1)
  5. Third layered texture (alpha map stored in the blue channel of Texture 1)

 

The textures once loaded are associated with the pixel shaders constant table for sampling, and the resulting constant table references are used in the texture stages. The constant table and texture stages are shown in Figure 5.

 

The HLSL pixel shader to perform the blending operation is displayed in Figure 4.

 

// Globals

sampler AlphaMap;

sampler BaseTex;

sampler TextureOne;

sampler TextureTwo;

sampler TextureThree;

 

// Structures

struct PS_INPUT

{

    float2 alphamap     : TEXCOORD0;

    float2 base         : TEXCOORD1;

    float2 textureone   : TEXCOORD2;

    float2 texturetwo   : TEXCOORD3;

    float2 texturethree : TEXCOORD4;

};

 

struct PS_OUTPUT

{

    vector diffuse : COLOR0;

};

 

// Main

PS_OUTPUT Main(PS_INPUT input)

{

    // zero out members of output

    PS_OUTPUT output = (PS_OUTPUT)0;

 

    // sample textures

    vector b = tex2D(BaseTex,      input.base);

    vector a = tex2D(AlphaMap, input.alphamap);

    vector i = tex2D(TextureOne, input.textureone);

    vector j = tex2D(TextureTwo, input.texturetwo);

    vector k = tex2D(TextureThree, input.texturethree);

 

    // combine texel colors

    float4 oneminusx = 1.0 - a.x;

    float4 oneminusy = 1.0 - a.y;

    float4 oneminusz = 1.0 - a.z;

    vector l = a.x * i + oneminusx * b;

    vector m = a.y * j + oneminusy * l;

    vector n = a.z * k + oneminusz * m;

 

    // save the resulting pixel color

    output.diffuse = n;

 

    return output;

}

 

Figure 4: HLSL implementation. Code a modification from Luna, F.D. (2003)

 

Firstly each textures is sampled to a pixel colour vector. The vector’s l,m and n are the linear interpolation blending implementation described above using the Alpha * Texture + (1-Alpha) * PreviousColour operation. These operations achieve the blending required.

 

AlphaMapHandle = CT->GetConstantByName(0, "AlphaMap");

CT->GetConstantDesc(AlphaMapHandle, &AlphaMapDesc, &count);

Dev->SetTexture(AlphaMapDesc.RegisterIndex, AlphaMap);

Dev->SetSamplerState(AlphaMapDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

Dev->SetSamplerState(AlphaMapDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

Dev->SetSamplerState(AlphaMapDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

 

Figure 5: Constant Table manipulation and Texture Stages for AlphaMap texture in HLSL implementation.

 

Assembly Implementation

 

Similarly with the HLSL implementation, the assembly implementation uses five textures as follows:

 

  1. Combined Alpha Map (R= Texture3 Alpha, G=Texture4 Alpha, B=Texture 5 Alpha, A=255)
  2. The base texture applied to the whole of the terrain chunk
  3. First layered texture (alpha map stored in the red channel of Texture 1)
  4. Second layered texture (alpha map stored in the green channel of Texture 1)
  5. Third layered texture (alpha map stored in the blue channel of Texture 1)

 

The assembly implementation does not require manipulation of the constant table and merely retrieves texture samplers from their respective texture stages. The texture stages for the assembly implementation are displayed in Figure 7. Figure 6 displays the assembly implementation.

 

ps_1_4

 

// Sample textures

// r0: combined alphamaps

// r1 - r4: textures

texld r0, t0

texld r1, t1

texld r2, t1

texld r3, t1

texld r4, t1

 

// Combine textures together based off of their alphamaps

mul r1, r1, r0.w

lrp r2, r0.x, r2, r1

lrp r3, r0.y, r3, r2

lrp r0, r0.z, r4, r3

 

Figure 6: Assembly implementation. Code a modification from Glasser, N. (2005)

 

The texld instructions sample the textures in their respective texture stages.

The mul instruction blends the base texture with the base alpha map stored in the alpha channel of the alpha texture (r0.w) This alpha channel is set to 255 (full alpha) and is rendered without blending.

The lrp dst, src0, src1, src2 instruction is the linear interpolation instruction which performs: 
dest = src0 * src1 + (1-src0) * src2 
 

As clear from the operation performed, the lrp instruction performs the desired blending of the three remaining textures, the final lrp instruction stores the result in r0 which is the resultant register and the final pixel colour.

 

Dev->SetTexture(0, AlphaMap);

Dev->SetTexture(1, BaseTex);

Dev->SetTexture(2, TextureOne);

Dev->SetTexture(3, TextureTwo);

Dev->SetTexture(4, TextureThree);

Figure 7: Texture Stages for the five textures in assembly implementation

 

Conclusion

 

We have outlined a fast and efficient method for implementing terrain texture blending in real time that is suitable for use on current GPU architectures and beyond. Using high detail base textures and intuitive alpha maps generation the resulting terrain texture effect can be quite impressive. Figure 8 shows an example 17x17 vertex terrain chunk rendered using this technique.

 

Figure 8: Terrain Texture Blending using assembler implementation

 

Care must be taken when generating alpha values at the edge of the alpha map when considering adjoining terrain chunks. Texture glitches may be visible on joining chunks if non-repeating terrain textures are used and thoughtful alpha map generation is not used.

 

References

 

  1. Bloom, C. (2000) "Terrain Texture Compositing by Blending in the Frame-Buffer" Online technical description (online: http://www.cbloom.com/3d/techdocs/splatting.txt; email: cbloom@cbloom.com)
  2. Corpes, G. (2001) "Procedural Landscapes" Proceedings GDCEurope 2001 (online: http://www.cix.co.uk/~glennc/gdcetalk_files/frame.htm)
  3. Glasser, N. (2005) "Texture Splatting in Direct3D" Online technical description (online: http://www.gamedev.net/reference/articles/article2238.asp; email: nglasser@charter.net)
  4. Luna, F.D. (2003) "Introduction to 3D Game Programming with DirectX 9.0" pp. 326-333 Wordware Publishing Inc. Plano, Texas.