Terrain Mesh Displacement using Vertex Textures

 

Tim Jenks

tim at jenkz dot org

http://www.jenkz.org/

October 2005

 

Abstract

 

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.

 

Introduction

 

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.

 

Related Works

 

Engel, W. (2002) Programming Vertex Shaders

 

Engel, W. provides an in-depth look into vertex shaders and how to use them efficiently.

 

Gerasimov, P et al.  (2004) Using Vertex Textures

 

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.

 

Introduction to Terrain Rendering

 

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.

 

Height Maps

 

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

 

Introduction to Vertex Textures

 

Vertex Texture Basics

 

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.

 

Vertex Displacement using Vertex Texture Fetch

 

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.

 

Assembly VS3.0 Implementation

 

Preface

 

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.

 

DirectX9.0 software vertex processing

 

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);

 

Texture Stages and Texture Creation

 

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);

 

Vertex Definition and Vertex Shader Constants

 

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

 

Assembly vs_3_0 Vertex Shader

 

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

 

Conclusion

 

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

 

References

 

  1. Engel, W. (2002) “Introduction to Shader Programming Part II: Programming Vertex Shaders” Gamedev.net article (online: http://www.gamedev.net/reference/articles/article1807.asp; email: wolf@direct3d.net)
  2. Hoppe, H. Asirvatham, A. (2005) "Terrain rendering using GPU-based geometry clipmaps" GPU Gems 2. pp. 27-45 Addison-Wesley.
  3. Gerasimov, P et al.  (2004) “Shader Model 3.0: Using Vertex Textures” Nvidia Whitepaper (online: ftp://download.nvidia.com/developer/Papers/2004/Vertex_Textures/Vertex_Textures.pdf)
  4. Ulrich, T (2002) “Rendering Massive Terrains using Chunked Level of Detail Control” Proceedings SIGGRAPH 02 (online: http://cvs.sourceforge.net/viewcvs.py/*checkout*/tu-testbed/tu-testbed/docs/sig-notes.pdf?rev=HEAD;