ComfyUI Node

Heightmap

Steal displacement-ready grayscale from anything

By criskb·Created 7 months ago·Updated 5 months ago· 0
Heightmap
  • image
  • source_mask
  • mask
  • image
  • mask
  • heightmap_info
source_mode
normalize_mode
value_min0.000
value_max1.000
percentile_low2.0
percentile_high98.0
gamma1.00
contrast1.00
blur_radius0.0
invert_valuesfalse
mask_feather8.0
invert_maskfalse

Heightmap is the node that turns any image - or just a channel of it - into the clean grayscale displacement map you'd feed a renderer, a normal-map baker, or a displacement-capable pass. It's the Surface/Maps sibling of x1Heatmap, minus the colors and plus a couple of map-specific knobs. If you've been opening images in Photoshop and manually desaturating + leveling to fake a heightmap, this does it deterministically, in the graph, with one node.

It sits in MKRShift Nodes/Surface/Maps, the pack's tech-art lane for deriving practical PBR-adjacent maps. Same family as x1IridescenceMap and the material nodes: "pull a scalar map out of pixels, normalize it, hand it to the next stage." No models, no downloads - just numpy math that runs in milliseconds.

How it works

Same skeleton as x1Heatmap: pick a source_mode (luma, red, green, blue, max_rgb, saturation, value, alpha, or mask), normalize, then output as grayscale instead of palette-mapped color.

The map-specific extras:

  • contrast - scales the grayscale ramp after normalization. This is your "does this displacement actually displace" control. A flat, low-contrast map will barely move geometry; crank contrast and the valleys get deeper.
  • blur_radius - smooths the map before output. Genuinely useful here in a way it isn't for preview tools: tiny displacement maps amplify noise into spikes, and a 1–3 px blur is the cheap, honest de-spiker.
  • normalize_mode with manual_range, auto_range, auto_percentile - same trio as the heatmap. auto_percentile (default 2–98) is the steady choice for a batch of frames; manual range if you need the map to mean the same thing everywhere.

gamma, invert_values, and mask_feather/invert_mask behave exactly as in the heatmap. One difference: Heightmap's optional inputs are source_mask and mask - the first restricts what gets analyzed, the second scissor-limits the final map.

Outputs

All three outputs are worth wiring somewhere: image is the grayscale heightmap, mask is the same values as a MASK tensor (some nodes downstream prefer masks to images), and heightmap_info reports the resolved source, the min/max the normalizer settled on, and mask coverage - read it when your map looks unexpectedly black or blown.

Installing it

Pack standard, no surprises:

cd ComfyUI/custom_nodes
git clone https://github.com/criskb/MKRShift_Nodes

then restart ComfyUI, or grab it via ComfyUI Manager by searching MKRShift_Nodes. No pip requirements, no model files for the map nodes.

Where people get burned

The most common mistake is assuming luma is always the right source. For displacement you often want value or a single channel, because luma's built-in color weighting squashes contrast that one channel preserves. And remember the output is already normalized - if you chain it into a normal-map generator that normalizes again, you're double-stretching and will flatten your shading. When in doubt, read heightmap_info; it tells you exactly what range the map left the node with.

CategoryMKRShift Nodes/Surface/Maps

Inputs (15)

NameTypeDefaultDescription
imageIMAGE
source_modeCOMBO9 options: luma, red, green, blue, max_rgb, saturation, +3
normalize_modeCOMBO3 options: manual_range, auto_range, auto_percentile
value_minFLOAT0.0000–1
value_maxFLOAT1.0000–1
percentile_lowFLOAT2.00–100
percentile_highFLOAT98.00–100
gammaFLOAT1.000.1–4
contrastFLOAT1.000.1–4
blur_radiusFLOAT0.00–128
invert_valuesBOOLEANfalse
mask_featherFLOAT8.00–256
invert_maskBOOLEANfalse
source_maskoptMASK
maskoptMASK

Outputs (3)

NameTypeDescription
imageIMAGE
maskMASK
heightmap_infoSTRING