Normal Maps from Height Maps: A Practical Guide for Indie Devs
When and how to use normal maps, strength vs Z scale, Sobel vs Scharr, and fitting results into Unity or Godot.
Normal maps encode surface direction for lighting; they're often generated from height maps (grayscale where white = high, black = low). Games use them for better shading without extra geometry. This guide covers strength, invert, Sobel vs Scharr, blur/sharpen, Z scale, and how to import the result into Unity or Godot so your materials look right in-engine.
Strength and Invert
Strength controls how much the gradient from the height map affects the normal. Higher strength means steeper slopes and more pronounced lighting variation. Invert flips the height so that what was bright becomes low and vice versa; use it when your source image has the opposite convention (e.g. bumps are dark). Start with a moderate strength (e.g. 4–6) and adjust until the lit result looks right in your engine.
If the normal map looks too flat in-game, increase strength; if it looks noisy or too extreme, decrease it. The height map's contrast also affects the result: a low-contrast height map will produce subtle normals even at high strength. Normalize your height map or increase contrast if you want stronger effect. Invert is essential when your source uses black-for-high (e.g. some displacement or bump sources); without it, bumps will appear as dents.
Quick tuning
Load the height map, set strength to 5, leave invert off unless bumps look inverted. Generate the normal and assign it in your engine; view under a directional light. Too flat → increase strength. Too noisy → add blur or reduce strength. Wrong direction (bumps as dents) → enable invert.
Sobel vs Scharr
These are gradient operators used to compute slope from the height map. Sobel is common and fast; Scharr is slightly more accurate for diagonal edges. The difference is subtle; try both and pick the one that gives cleaner normals for your asset. Blur (positive) and sharpen (negative) can smooth or accentuate detail before the normal is computed.
Blur reduces high-frequency noise and can make the normal map look softer; useful when the height map has grain or when you want a smoother lit result. Sharpen (negative blur) accentuates edges and can make details pop, but can also amplify noise. For most game assets, Sobel or Scharr with default or light blur is a good starting point; tune in-engine and compare.
Z Scale and AO
Z scale sets how much the normal vector points "out" of the surface. Higher Z makes the surface react more to lights from the front. Displacement and ambient occlusion (AO) are often generated from the same height map: displacement for parallax or tessellation, AO for crevice darkening. Export all three (normal, displacement, AO) if your pipeline supports them; otherwise normal alone is enough for basic lit look.
Z scale is especially relevant when the normal is used in a shader that derives lighting from the normal's Z component; too low and the surface can look flat under front lighting. Displacement maps are used for parallax occlusion mapping or actual vertex displacement; AO is typically multiplied with ambient or baked lighting to darken crevices. If your tool supports exporting multiple outputs from one height map, generating normal + AO (and optionally displacement) in one pass keeps the pipeline consistent.
Importing in Unity or Godot
Import the normal map as a texture. In Unity, assign it to the Normal Map slot of a material; ensure the texture is marked as Normal map so the shader interprets it correctly. In Godot, use the normal map in the material's normal slot. Both engines expect the normal in tangent space; our tool outputs tangent-space normals (R=X, G=Y, B=Z in the usual convention).
In Unity, select the texture and in the Inspector set the type to Normal map; the import pipeline may apply optional encoding. In Godot, assign the texture to the Normal Map slot of a SpatialMaterial or similar; the engine expects tangent-space normals by default. If you see incorrect lighting (e.g. wrong direction), check that the normal map wasn't generated in a different space (e.g. object space) and convert if needed. Tangent-space is the standard for character and environment art that may be rotated or animated.
Summary
Generate normals from height with strength and invert as needed; choose Sobel or Scharr and optional blur/sharpen. Set Z scale and consider exporting AO or displacement from the same height. Import as a normal map in Unity or Godot and assign to the material's normal slot for a lit, detailed look without extra geometry.