Terrain meshes in computer graphics are used to visually represent a Digital Elevation Model, often used to create outdoor environments in computer games or visualizations in Geographic Information Systems. Traditionally the whole vertex mesh is stored as static vertex data in video memory, however modern day programmable hardware allows for the whole terrain to be rendered using a single small static vertex block. We propose a simple method to render a large terrain mesh using a programmable GPU and vertex textures for height displacement.
Traditionally terrain rendering is achieved by storing the whole terrain mesh as static vertex data – split into blocks or chunks. The height data, or Digital Elevation Model of the area of terrain in question is generally stored as a greyscale image; with 0 values being low-lying land and 255 values being high land. This height data is then pre-processed ready for level-of-detail rendering, but generally every vertex in the mesh stored in video memory.
The advent of Programmable Graphics Processing Units allows us to store one master block in video memory, for example a 17x17 “chunk”. This single block can be used to render the whole terrain by displacing the height of the vertices in the block using Vertex Textures.
Vertex Texture Fetch as described by Gerasimov, P et al. (2004) allows Vertex Shader 3.0 to sample a texture – similar to how pixel shaders can sample a texture. This lets the vertex shader read much more data than previously possible. The technique is most commonly used to displace vertices in a mesh – to, for example, alter the height of a vertex in a mesh.
Engel, W. provides an in-depth look into vertex shaders and how to use them efficiently.
This Nvidia white paper describes the use of Vertex Textures in a vertex shader for vertex displacement to create, for example, wave effects on an ocean. Basic implementations are described alongside blending and mipmap selection.
Hoppe, H. Asirvatham, A. (2005) Terrain rendering using GPU-based geometry clipmaps
Hoppe, H. explains how to implement his geometry clipmap terrain rendering algorithm entirely upon a programmable GPU by using a small number of fixed “footprint” vertex blocks. These blocks are translated into position in the mesh by the vertex shader. Vertex Texture Fetch is used to retrieve the height data for the relevant block.
Terrain Blocks
A large terrain of, for example, 512x512 vertices, is too large to be rendered as one single mesh. Most modern terrain rendering algorithms partition the terrain mesh into smaller blocks, or chunks. These blocks or chunks can then frustum-culled so not to render off-screen areas of the terrain. Figure 1 shows a large terrain split into 16 blocks.

Figure1: A terrain mesh
divided into blocks. Image Ulrich, T (2002)
It is clear from Figure1 that the vertex data for each block is different – each block has a different terrain topology. Traditionally this unique vertex data for every block is stored in static video memory.
We propose instead of storing vertex data for every terrain block, one single master block is stored in video memory containing no height data. When rendering the whole mesh, this master block is rendered multiple times, translated into position using a vertex shader. The height of each vertex in the block is displaced using Vertex Textures creating the desired terrain topology.
Terrain height data is generally stored as a greyscale image – where low intensities represent low lying-land, and bright intensities represent high land. The application pre-processes this data and creates the vertex data for each block in the terrain mesh. Figure 2 shows a greyscale height map.

Figure 2: A greyscale height
map
Vertex Shader 3.0 allows the vertex shader to sample a texture similarly to the pixel shader. Unlike the pixel shader for creating texture effects, however, the vertex shader can use the data in this texture for many effects otherwise unavailable in the Fixed Function Pipeline, including - displacing vertices.
Vertex Textures can either be single channel or four channels – allowing for up to 4 floating-point values to be passed to the vertex shader for any particular vertex. These floating-point values can be used for anything – in this instance they will be used to displace the height of the vertex.
To displace a vertex in a mesh the Vertex Texture must be sampled. This is achieved in the same manner as a pixel shader: a texture sampler interface is queried using texture coordinates and the result colour or vector4 is stored.
The vertex output position is then manipulated using the colour values from the sampler query.
Implementation specifics can be seen in the implementation section of this article.
At the time of writing this article Vertex Shader 3.0 support, and notably Vertex Texture Fetch support is limited to a small number of graphics cards. However, vertex shader 3.0 can be emulated in DirectX software vertex processing.
Only R32F and A32B32G32R32F texture formats are supported by most hardware. It is possible to query the device capabilities for a specific vertex texture format to verify if a specific format is supported on your hardware.
Software vertex processing can be enabled, if required, in the following manner:
CreateDevice(
D3DADAPTER_DEFAULT, // primary adapter
deviceType, // device
type
hwnd, //
window associated with device
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
// vertex processing
&d3dpp,
// present parameters
device);
When using software vertex processing the texture to be sampled must be created in the scratch pool (i.e. discard-able system memory) D3DXPOOL_SCRATCH. In software processing all texture formats are supported. To create a texture in the scratch pool use:
D3DXCreateTexture(Dev, texWidth, texHeight, 0, 0,
D3DFMT_X8R8G8B8, D3DPOOL_SCRATCH, &Texture);
Software processing for this example is used, height data is stored in the red channel of an R32F texture:
D3DXCreateTexture(Dev,
texWidth, texHeight, 0, 0, D3DFMT_R32F, D3DPOOL_SCRATCH, &BaseTex);
D3DLOCKED_RECT lockedRect;
BaseTex->LockRect(0,
&lockedRect, 0, 0);
float*
imageData = (float*)lockedRect.pBits;
for(int i = 0; i <= texHeight; i++) {
for(int j = 0; j <= texWidth;
j++) {
float height = getVHeightAt(i,j);
imageData[j *
lockedRect.Pitch / 4 + i] = height;
}
}
BaseTex->UnlockRect(0);
For vertex texture sampling the texture must be set to one of the special vertex texture stages D3DVERTEXTEXTURESAMPLER[0-3]. Additionally, there are restrictions to what texture filters can be used for vertex textures, it is possible to query the device capabilities for vertex texture filter support. To set up the texture stage and to disable texture filtering the following DX9.0 API calls are made:
Dev->SetTexture(D3DVERTEXTEXTURESAMPLER0,
BaseTex);
Dev->SetSamplerState(D3DVERTEXTEXTURESAMPLER0,
D3DSAMP_MINFILTER, D3DTEXF_POINT);
Dev->SetSamplerState(D3DVERTEXTEXTURESAMPLER0,
D3DSAMP_MAGFILTER, D3DTEXF_POINT);
Dev->SetSamplerState(D3DVERTEXTEXTURESAMPLER0,
D3DSAMP_MIPFILTER, D3DTEXF_NONE);
A basic Flexible Vertex Format is used for this implementation and is defined as:
#define
D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |
D3DFVF_TEX1)
The vertex shader input registeres will therefore be:
v0 = position
v1 = texture coordinates
A single vertex shader constant is set up for transforming the vertices into screen space, the transpose of the view projection matrix.:
D3DXMATRIXA16 mat;
D3DXMatrixMultiply( &mat,
&c->getViewMatrix(), &c->getProjectionMatrix() );
D3DXMatrixTranspose(
&mat, &mat );
Dev->SetVertexShaderConstantF(
0, (float*)&mat, 4 );
The vertex shader constant registers will there for
be:
c0 to c3 = Transpose of the view*projection matrix
Figure 3
lists the assembly vertex shader implementation
vs_3_0
dcl_2d s0 // Input: Vertex Texture Sampler
dcl_position v0 // Input: Vertex position in world space
dcl_texcoord v1 // Input: Vertex texture coordinates
dcl_position o0 // Output: Transformed vertex in screen space
dcl_color o1 // Output: Vertex Colour
def c40, 2.5f, 1.0f, 1.0f, 1.0f // Temp: scale
vector – scale
// height of terrain by 2.5
def c41, 0.0f, 0.0f, 0.0f, 1.0f // Temp: Colour of vertex i.e.
// black
texldl r1.x,v1,s0 // Sample texture to r1 using texcoords in v1
mul r1.x, r1.x, c40.x // Multiply (ie Scale) texture data by c40
mov r0,v0 // Initialize r0 as vertex position in v0
mov r0.y,r1.x // Displace height(y) by texture data in r channel
m4x4 o0,r0,c0 // Multiply vertex in r0 by transpose of view*proj
// and output to output position register o0
mov o1,c41 // Move vertex colour data in c41 to output color
// register o1
Figure 3: Assembly vs_3_0
implementation
For readers
unfamiliar with vertex shader 3.0 registers and instructions a brief outline
are provided as comments.
The vertex
shader samples the vertex texture in D3DVERTEXTEXTURESAMPLER0
using the texture coordinates defined in the vertex data. The mul
instruction to scale the terrain height is optional – and should be modified
depending on the heights of the terrain desired.
The data from the vertex texture then displaces the
height (y coordinate) of the vertex. Finally the vertex is transformed to
screen space by multiplcation by the transpose of the view * projection matrix
We have
provided a basic implementation for displacing the height data of a terrain
block. The implementation can easily be adapted for the rendering of
large-scale terrains. Results of the displacement can be seen in Figure 4.

Figure
4: Top: Master block. Bottom: Master block with vertex texture displacement