DEV Community

Veronica Lin
Veronica Lin

Posted on

Normal Maps Explained for Game Devs: Turn a Flat Texture into Surface Detail

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:

  1. Baked from high-poly geometry (Blender, Substance, xNormal). Best quality, heaviest workflow.
  2. 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:

  1. Preview with moving light. If the bumps look like craters, your height map is inverted (or you picked the wrong convention).
  2. Watch the seams. On tiling textures, crank edge handling; naive conversion brightens seams at tile boundaries.
  3. Strength is not fidelity. Overshooting strength produces that shiny "gravel" look. If everything looks wet, turn it down.
  4. 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)