
Bump maps and normal maps do the same job — fake fine surface detail without geometry — and they look almost identical when you’re facing the surface head-on. Turn the camera 60 degrees and the difference becomes obvious: the bump map flattens out and the normal map keeps its detail. That’s the whole story in one sentence. The rest is the why.
The one-line difference
- Bump map — a greyscale image. Each pixel stores one number: height. The renderer infers light response from luminance differences between pixels.
- Normal map — an RGB image. Each pixel stores three numbers: an (X, Y, Z) surface direction. The renderer computes light response directly from the vector.
Three times the data per pixel, and that’s exactly where the quality comes from.


Why bump maps break at glancing angles
A bump map only knows how high each pixel is, not which way it’s facing. The shader infers a direction from the height difference between neighbouring pixels (basically a gradient). That works when you’re looking straight down at the surface — the inferred direction is close enough.
Tilt the camera and the inference falls apart. The shader is reading a tiny vertical slice of the height map and trying to guess a 3D direction from it. The surface looks like it’s been ironed flat. Normal maps carry the direction information directly, so glancing angles look correct.

Memory and performance
A normal map costs about three times more storage uncompressed because it’s RGB instead of greyscale. After GPU compression the gap closes: BC5 compresses normals at about 1 byte per pixel; BC4 compresses bumps at about 0.5 bytes per pixel. So a 2048×2048 normal map weighs ~4 MB on the GPU, a bump map ~2 MB. On modern hardware this is rounding error.
When to use each
Use a normal map when:
- You’re shipping a PBR material to Unity, Unreal, Blender, Godot, or Three.js
- The camera will see the surface from varied angles
- You have the budget to bake or craft one (which, with AI, is “always”)
Use a bump map when:
- You’re prototyping and just need a stand-in
- You already have a clean height map you don’t want to re-bake
- You’re layering fine grain (pores, hairline scratches) on top of an existing normal
Combining them — the layering trick
You can stack a bump map on top of a normal map for very fine grain. The normal handles the main surface shape; the bump adds dust-and-pores detail that would be wasted at normal-map resolution. Implementations:
- Blender — the Bump node has a Normal input; chain them.
- Unreal Engine — use the BlendAngleCorrectedNormals material function.
- Unity — sample a normal from a bump (via NormalFromHeight) and add it to your main normal.
- Three.js — use both
normalMapandbumpMapon aMeshStandardMaterial; the shader blends them.
Bump → normal: every modern engine does the conversion
Plug a bump map into Unity, Unreal, or Blender and internally it gets converted to a normal map at shader time. You’re paying the cost of the conversion every frame instead of paying it once at bake. For a single material it’s negligible; across a scene with hundreds of bump-only materials it adds up.
Bake it once with the right tool and you ship a normal map. CraftPBR’s normal map crafter does this in the browser — drop a photo or height map, get back a tangent-space normal map with proper Y convention for your engine.
The OpenGL vs DirectX gotcha (applies to both)
Whichever map you ship, the green channel convention applies. OpenGL expects Y up; DirectX expects Y down. Blender and Substance Painter default to OpenGL; Unreal expects DirectX; Unity lets you pick. If your surface looks lit from the inside out, flip the green channel. (See the full normal map guide for the long version.)

Key takeaways
- Bump = greyscale height; normal = RGB direction
- Normal maps look right at every angle; bump maps flatten at glancing angles
- After compression the memory difference is rounding error
- Use bumps for fine grain on top of a normal — they layer beautifully
- Every modern engine converts bumps to normals at shader time, so you may as well bake the normal once