Archive for January, 2008

Converting Displacement Maps into Normal maps

This is a repost from the old blog, since this is one of those interesting stuff, which weren’t big enough to warrant a sample.

Working on my VTF tutorial, I came across the need to apply lighting to a terrain. For lighting, we need normals, of course, but I only had heightmaps available. When the terrain is static, this poses no problems at all. All we have to de is generate a normal map from the heightmaps with whatever tools we want. For example, NVIDIA has a Photoshop plugin for this here.

However, when we have dynamic terrain, with real time deformation, this is no longer an option. This is especially the case when we make the deformations on the displacement map. After lots of reading and searching, I’ve come across the Sobel Filter, which can be used to compute normal maps.

As a result, I made a pixel shader that takes as input the displacement map and outputs the normal map. At runtime, after modifying the displacement map, I apply this shader and put the result into a render target. This result is then used when drawing the terrain, when computing lighting in the pixel shader.

So here’s the HLSL code:

float textureSize = 256.0f;
float texelSize =  1.0f / textureSize ; //size of one texel;
float normalStrength = 8;
 
float4 ComputeNormalsPS(in float2 uv:TEXCOORD0) : COLOR
    {
    float tl = abs(tex2D (displacementSampler, uv + texelSize * float2(-1, -1)).x);   // top left
    float  l = abs(tex2D (displacementSampler, uv + texelSize * float2(-1,  0)).x);   // left
    float bl = abs(tex2D (displacementSampler, uv + texelSize * float2(-1,  1)).x);   // bottom left
    float  t = abs(tex2D (displacementSampler, uv + texelSize * float2( 0, -1)).x);   // top
    float  b = abs(tex2D (displacementSampler, uv + texelSize * float2( 0,  1)).x);   // bottom
    float tr = abs(tex2D (displacementSampler, uv + texelSize * float2( 1, -1)).x);   // top right
    float  r = abs(tex2D (displacementSampler, uv + texelSize * float2( 1,  0)).x);   // right
    float br = abs(tex2D (displacementSampler, uv + texelSize * float2( 1,  1)).x);   // bottom right
 
    // Compute dx using Sobel:
    //           -1 0 1 
    //           -2 0 2
    //           -1 0 1
    float dX = tr + 2*r + br -tl - 2*l - bl;
 
    // Compute dy using Sobel:
    //           -1 -2 -1 
    //            0  0  0
    //            1  2  1
    float dY = bl + 2*b + br -tl - 2*t - tr;
 
    // Build the normalized normal
    float4 N = float4(normalize(float3(dX, 1.0f / normalStrength, dY)), 1.0f);
 
    //convert (-1.0 , 1.0) to (0.0 , 1.0), if needed
    return N * 0.5f + 0.5f;
}

You should tweak normalStrength until the result you get is satisfactory.

And here is the result of applying that code to two heightmaps, and some terrain lit using a normal map generated by this shader.

 

displacement normals

HLSL Learning Resources

In response to some people asking me how to start learning shader programming, I made a list of some resources that could prove helpful when learning HLSL:

The Shader Series, found on creators.xna.com are a very good set of articles and samples, that explain the basic knowledge needed to understand and write shaders in HLSL.

Riemer Grootjans also has a series of tutorials on HLSL, which can be found on his site.

A good book on shaders is The Microsoft DirectX 9 Programmable Graphics Pipeline by Kris Gray. I found this book extremely useful, with very good explanations, and nicely structured. The code samples, however, are in C/C++, but for most people that shouldn’t be a problem.

After mastering the basics, you can explore other samples and articles, which can be found on www.ziggyware.com and creators.xna.com.

Another good place to find information on shaders are the web pages of the two big GPU manufacturers, NVIDIA and ATI. Each site has a section devoted to developers, which are great places for everyone, beginners are advanced users alike. Both sites provide code samples, documentation, tools and SDK’s. Out of these, my favorites are NVIDIA Shader Library, NVIDIA SDK and ATI SDK. Most important, these site contains lots of articles and presentations from various conferences, which explain a wide range of effects and techniques, and how they are achieved on the GPU. Admittedly, most of them do not provide HLSL code, but the idea and algorithm is almost always more important than the code itself.

For advanced effects and usages of the GPU, I recommend the GPU Gems series and the ShaderX series.

Finally, as a recommendations for those who want to learn HLSL, try to examine as many samples as possible, and play with the HLSL code to see what happens when changing values, until you understand why things happen the way they do. Don’t start by doing HDR Lighting, or Relief Mapping. Start with the small tutorials, and understand what happens and why before going on to the more spectacular samples. Last, but not least, don’t be afraid to look at C/C++ samples. The DirectX SDK contains some very good samples of HLSL. I know, they’re written in C/C++, but the shader code stays the same when going from C++ to XNA.