Nodes/ComfyUI-TextureAlchemy/Height to Normal Converter
ComfyUI Node

Height to Normal Converter

Your height map wants to be a normal map. Let it.

By amtarr·Created 9 months ago·Updated 4 months ago· 58
Height to Normal Converter
  • height
  • normal
  • format_info
strength1.0
methodscharr
output_formatDirectX
blur_radius0.0

Here's a trick a lot of people don't realize until they're elbow-deep in material extraction: you almost never need to generate a normal map from scratch. If you have a height map, the normal map is just the gradient of that height - a couple of derivative filters and you're done. That's exactly what Height to Normal Converter does, and it's one of the more useful nodes in the TextureAlchemy pack for the plain reason that most AI depth pipelines hand you height, not normals.

In the pack's own recommended workflow it's placed right after the Lotus height output, generating normals from the same data so the material set has both maps without running a second model. That's the whole pitch: free normals, GPU-fast, no extra checkpoint, no VRAM tax.

How it works

The node takes a height image, drops it to grayscale, and runs a gradient operator across it - Sobel, Scharr, or Prewitt - to compute how much the surface rises in x and y at every pixel. Those two slopes become the red and green channels of the normal map; blue encodes "facing the camera," which the math sets automatically. It's the textbook approach, and the source even labels the operators honestly: scharr is the sharpest default, sobel is the standard balanced pick, prewitt is the smooth/simple option. The strength slider scales the slopes before the blue channel is rebuilt - crank it past 1 to exaggerate bumps, drop it toward 0 to flatten.

Two inputs matter beyond that. blur_radius (0–5) pre-blurs the height map with a Gaussian, which is your noise killer: AI depth maps are noisy, and that noise becomes spikes in the normal map, so a small blur radius is often the difference between usable and garbage. And output_format picks which way "up" points - OpenGL for Unity/Blender, DirectX for Unreal/Maya. This is the single most common source of "why does my lighting look inverted" confusion, and it's a dropdown, so you have no excuse.

Outputs are normal (the map) plus a format_info STRING that tells you what format it was written as - handy when you're feeding a pipeline that needs the answer recorded.

The inputs that matter

  • method - default scharr is genuinely the best-looking; drop to sobel if you want softer results.
  • output_format - match your game engine. Get this wrong and the whole map looks wrong even though nothing is broken.
  • blur_radius - start at 0 and add 0.5–1.0 only if the output looks spiky.
  • strength - 1.0 is faithful; 1.2–1.5 is a common exaggerate setting for game assets.

Installing it

Part of ComfyUI-TextureAlchemy - install once and you get all the nodes. In ComfyUI Manager search "TextureAlchemy", install, restart. Manual:

cd ComfyUI/custom_nodes
git clone https://github.com/amtarr/ComfyUI-TextureAlchemy

Restart and find it under Texture Alchemist → Normal. No extra pip packages, no model downloads - this one is pure torch math. (If you want the AI height source itself, you'll need Kijai's ComfyUI-Lotus separately; the README lists it as the optional upstream.)

Common issues

  • "My normal map looks inverted!" Ninety percent of the time that's a format mismatch, not a broken node. Check output_format against your target engine, or run the pack's Normal Format Validator to see which way your map actually points.
  • Spiky, noisy output. Bump blur_radius to 0.5–1.5 and re-run. AI height maps are the usual culprit.
  • Barely-there detail. Raise strength; the map is faithful but if the source height is flat-ish, faithful looks flat.

One honest caveat: a height-derived normal map is only as good as the height map. Fine micro-detail that the height doesn't contain won't magically appear. For that you'd overlay a detail normal - but as a cheap, fast way to round out a PBR set, this node does exactly what it says.

CategoryTexture Alchemist/Normal

Inputs (5)

NameTypeDefaultDescription
heightIMAGE
strengthFLOAT1.00–10Normal map strength/intensity
methodCOMBOscharrGradient operator: Scharr (best), Sobel (standard), Prewitt (simple)
output_formatCOMBODirectXOpenGL: Y+ up (Unity, Blender) | DirectX: Y- up (Unreal, Maya)
blur_radiusFLOAT0.00–5Pre-blur height map to reduce noise (0 = no blur)

Outputs (2)

NameTypeDescription
normalIMAGE
format_infoSTRING