Transforming Normal Maps

It’s very straightforward in games development to rotate a texture map. Photoshop and even many simple file navigators make this a trivial task. However, rotating a normal map completely invalidates it using these means. The colors on a normal map represent the 3-dimensional reaction that light has when it reaches the object’s surface. The game engine interprets the value to tint the pixel on the rendered image in order to add detail onto what would be a flat surface. And so rotating the map is fine for diffuse maps, but with normal maps, the lighting is misrepresented on the new map.

Example

This is our original map:
original
If we rotate the map in photoshop, we get this:
wrong
This would yield incorrect results in a renderer. In the original map, the top edges are lined in purple, with the sides in pink and turquoise and the bottom side of the letters is lined with a cyan color. However, the rotated image puts these colors on the wrong sides of the letter.

This is what we are trying to achieve:
ideal
Note that the edges now show the correct colors.

The Solution

Fixing a rotated normal map is a case of playing around with the map channels.
After having performed the rotation in photoshop, we can fix the normals by meddling with the color channels, using a combination of swapping color channels, and inverting the individual channels. Note that the blue channel always remains untouched.

transform Red Channel Green Channel Blue Channel
rotate clockwise G inverted R B
rotate anti-clockwise G R inverted B
rotate 180 R inverted G inverted B
flip horizontal R inverted G B
flip vertical R G inverted B

Tools

So how do we go about achieving this? CG Society user, sinistergfx has produced this useful set of photoshop actions for performing normal map transforms: link

Or if you need to perform this programmatically, I put together a static class for manipulating an Image object directly…


public static class NormalMapTransform
{
public static void Rotate90CW(Image inputImage)
{
inputImage.RotateFlip(RotateFlipType.Rotate90FlipNone);
int width = inputImage.Width;
int height = inputImage.Height;

for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Color pixel = ((Bitmap)inputImage).GetPixel(x, y);
int rValue = 255 - pixel.G;
int gValue = pixel.R;
int bValue = pixel.B;
((Bitmap)inputImage).SetPixel(x, y, (Color.FromArgb(rValue, gValue, bValue)));
}
}
}
}


You should be able to work out the other transform methods using the table!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s