If you've ever slapped a high-res texture on a low-poly model and felt something was missing, the missing piece is usually surface detail — the bumps, grooves and ridges that catch light differently as the camera moves. That's exactly what normal maps fake, and you don't need a DCC license to make one.
What a normal map actually stores
A normal map is an RGB image where each pixel encodes a surface direction instead of a color:
- R = X axis (left/right tilt)
- G = Y axis (up/down tilt)
- B = Z axis (facing out of the surface)
Your shader reads these directions and perturbs the lighting normal per-pixel, so a flat quad can shade as if it had real geometry. The silhouette stays flat — normal maps don't change the outline — but the lighting response is convincingly 3D.
Two conventions exist for the green channel:
- OpenGL: Y+ points up (the Blender/Maya/WebGL convention)
- DirectX: Y+ points down (the Unreal/Unity default)
Feeding one convention to an engine expecting the other is the classic "my normal map looks inverted" bug. It's a one-line fix — flip the green channel — but you need to know which one you have.
Where normal maps come from
There are two common sources:
- Baked from high-poly geometry (Blender, Substance, xNormal). Best quality, heaviest workflow.
- Converted from a grayscale height/bump image. The converter estimates slopes from brightness differences and turns them into normals. That's 90% of what you need for props, walls, ground and anything tiling.
For source #2, the pipeline used to be: open GIMP, install a normal-map plugin, fiddle, export. For quick iterations that's friction you don't need.
A browser-based alternative
I've been maintaining a small free tool for the second workflow — Normal Map Generator. It runs entirely in the browser:
- Drop in any image (height map, grayscale photo, even a diffuse texture)
- Live sliders for strength, smoothing, edge handling and a lighting preview so you see the result on a 3D-ish surface before exporting
- Choose OpenGL or DirectX output up front instead of discovering the convention mismatch later
- Nothing is uploaded — processing happens locally, which matters when your texture is unreleased game art
Typical use case: you generated a seamless stone texture, you need a matching normal map now, and you're 40 minutes from a build deadline. Drop it in, push strength until the grooves read, export, done.
Quick sanity checks after generating one
Whatever tool you use, verify:
- Preview with moving light. If the bumps look like craters, your height map is inverted (or you picked the wrong convention).
- Watch the seams. On tiling textures, crank edge handling; naive conversion brightens seams at tile boundaries.
- Strength is not fidelity. Overshooting strength produces that shiny "gravel" look. If everything looks wet, turn it down.
- Silo check. Remember: the silhouette is unchanged. Normal maps carry lighting detail; if the outline needs to change, that's geometry or a displacement map's job.
Takeaway
Normal maps are the highest value-per-pixel trick in real-time rendering, and the height-image conversion route covers most day-to-day asset work. Bake from high-poly when you're hero-ing a character; convert from grayscale when you're shipping 200 environment props. Match the OpenGL/DirectX convention to your engine, preview before you export, and keep the strength honest.
Top comments (0)