Normal maps: what they are and how they fake detail

Texturing & Materials

A texture that stores surface direction rather than colour, so lighting reacts to detail the mesh does not have.

A normal map is a texture that stores which way the surface faces at every pixel, instead of what colour it is. The renderer lights each pixel using that stored direction rather than the direction of the triangle underneath, so a flat polygon shades as if it had grooves, rivets or pores it does not actually have.

How does a normal map store a direction?

Each pixel holds a vector in its three colour channels: red is X, green is Y and blue is Z. Each channel maps 0–1 onto −1 to +1, so a value of 0.5 means zero. A surface pointing straight out is (0, 0, 1), which stores as (0.5, 0.5, 1.0) — the pale blue-violet that every tangent-space normal map is mostly made of. Anything that is not that colour is a slope.

Why do most normal maps look blue?

Because they are in tangent space: each direction is measured relative to the surface it sits on, so "straight out" is the same blue everywhere. That is what lets one map work on a mesh that bends, deforms under a skeleton, or repeats the same texture across many surfaces.

Object-space normal maps measure directions relative to the whole object instead, and look like a rainbow. They are slightly cheaper to evaluate but break the moment the mesh deforms, so they are rare outside static props.

Why does my normal map look inverted?

Almost always the green channel. Two conventions exist and they disagree only on Y:

  • Y+ ("OpenGL") — green points up. glTF uses this: its specification defines tangent-space normals with +X right, +Y up and +Z toward the viewer. Blender, Unity and Godot expect it too.
  • Y− ("DirectX") — green points down. Unreal Engine expects this one.

A map in the wrong convention makes raised detail look sunken under one light direction and correct under another, which is why it survives a quick look. Flip the green channel on import rather than editing the texture.

Does a normal map need special import settings?

Yes. It is data, not colour, so it must be read with a linear transfer function, never as sRGB. glTF requires this outright; in Unity it means setting the texture type to Normal map, and in Blender setting the image to Non-Color. Read as sRGB, every direction is skewed and the lighting looks subtly wrong everywhere.

Engines also compress normal maps differently from colour. A common choice is BC5, which keeps only X and Y at good precision and reconstructs Z, since a unit vector's third component follows from the other two.

What can't a normal map do?

  • Change the silhouette. The outline is still the mesh's outline, so a "bumpy" sphere has a perfectly smooth edge.
  • Hold up at grazing angles. Seen nearly edge-on, the illusion flattens out.
  • Occlude or cast shadows. Detail does not hide what is behind it. That needs parallax or real displacement.

Normal map, bump map or displacement?

  • Bump map — a greyscale height image; the renderer works out the slopes from it. Simpler, less precise.
  • Normal map — the slopes stored directly. The standard for real-time work.
  • Displacement — actually moves vertices, so it changes the silhouette. Needs the geometry to move.

Where normal maps come from

The accurate way is baking: projecting a detailed high-poly mesh onto a simpler one. A normal map can also be derived from a colour texture — what PBR Channels does — which is quick and approximate — it reads light and dark as height, so a painted shadow can come back as a dent. Use a derived map for surface texture, and bake when the detail is real geometry.

Sources

Last reviewed