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.
This is our original map:
If we rotate the map in photoshop, we get this:
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.
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|
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)
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!