Normal Map Generator
Turn a heightmap — or any texture with usable luminance detail — into a tangent-space normal map. Brightness is read as height, the surface slope is measured with a Sobel filter, and each pixel's surface normal is encoded into RGB. Drop a brick texture in and get the bumps a shader needs to light it.
Drop a heightmap or texture here
Multiple files allowed
How a normal map is built
- Drop a heightmap. White is high, black is low — tick invert if your source uses the opposite convention.
- Set the strength. This scales the measured slope. 1–3 suits most surface detail; above 8 you get exaggerated relief that will look plastic under a moving light.
- Add a little smoothing if the source is noisy or has JPEG artefacts — the gradient operator amplifies pixel noise into visible speckle in the normal map.
- Flip green if your engine needs it (see below), then download the result.
Each pixel's luminance becomes a height value. A 3×3 Sobel kernel measures how fast that height changes
horizontally and vertically — Sobel rather than a plain neighbour difference because it averages across three
rows, which suppresses noise. The two gradients plus a fixed Z give a surface normal, which is normalised to
unit length and packed into RGB with the standard value × 0.5 + 0.5 mapping. That is why an
unbumped area comes out as the familiar flat lavender, #8080ff — the normal there points straight
up.
OpenGL versus DirectX green
The two conventions disagree about which way the green channel points. OpenGL (+Y up) is what Blender, Godot, Substance and most of the glTF ecosystem expect, and is the default here. DirectX (−Y up) is what Unreal Engine and some Unity setups want. Get it wrong and the lighting inverts — bumps read as dents and the surface looks lit from the wrong side. It is the most common normal-map mistake and the easiest to fix: tick the box.
What this can and cannot do
Deriving normals from an image is an approximation. A true normal map is baked from high-poly geometry, where the normals are known rather than inferred. What you get here is surface detail — the grout lines in tile, the weave in fabric, the grain in wood — which is exactly what a heightmap-derived map is good for, and what most texture work needs.
It cannot invent shape the luminance does not describe. Feeding it a photo means every dark region reads as low ground, so a shadow becomes a crater and a painted black line becomes a groove. Feeding it a colour texture means hue variation with no relief still produces bumps. When the source is a photograph, desaturate and remove the lighting first, or author the height channel deliberately.
FAQ
What should I feed it?
A greyscale heightmap is ideal. A tileable texture works well. A photo with baked-in lighting works badly. If you only have a colour texture, convert it to grayscale first and check that the result reads as height rather than as albedo.
Does the output tile seamlessly?
Edge pixels are clamped rather than wrapped, so a seamless source gets a faint discontinuity at the border. For tiling textures, build a seamless source with Seamless pattern first, or generate at a larger size and crop.
Why is my normal map noisy?
The gradient operator amplifies whatever high-frequency detail is in the source, including compression artefacts. Raise the smoothing to 1–2 px, or start from a PNG rather than a JPG.
Is the alpha channel used?
No. The output is fully opaque, which is what shaders expect from a normal map. Some pipelines pack height or roughness into alpha — that has to be done deliberately in your texture tool.