Nodes/MKRShift_Nodes/AO From Height
ComfyUI Node

AO From Height

Ambient occlusion from a heightmap, with no bake farm

By criskb·Created 7 months ago·Updated 6 months ago· 0
AO From Height
  • image
  • source_mask
  • mask
  • image
  • mask
  • ao_info
◄source_mode▾►
◄output_mode▾►
◄normalize_mode▾►
◄value_min0.000►
◄value_max1.000►
◄percentile_low2.0►
◄percentile_high98.0►
◄radius8.0►
◄intensity2.00►
◄gamma1.00►
◄invert_heightfalse►
◄mask_feather8.0►
◄invert_maskfalse►

Ambient occlusion is the difference between a flat material and one that sits in its own shadows - the darkening in creases, under edges, where surfaces almost touch. Normally you bake that in a DCC tool or a real renderer. x1AOFromHeight approximates it from a heightmap in a fraction of a second, which makes it the "good enough" AO for roughing out materials in ComfyUI without leaving the graph.

It's from MKRShift Nodes' Surface/Tech Art branch, and it's worth saying plainly what the author says in the docs: this is a fast artist-driven approximation, not a physically accurate ray-traced solve. It will not win you a lighting bake. It will give you a believable occlusion map for preview, wear, and packing workflows in seconds.

How it works

You feed it a height-like source and it compares each pixel against its local neighborhood over a radius to figure out where the surface recedes - that's the crease signal that becomes occlusion. source_mode decides what counts as height: luma, any RGB channel, value, alpha, or an external source_mask. output_mode lets you choose between ao (bright-open / dark-occluded - the classic) and cavity (the raw cavity signal, for tighter detail-driven work). intensity scales the result, gamma shapes it, invert_height flips the source if your heightmap is inverted. Then normalize_mode (with auto_percentile as the sane default, or manual_range when you need a locked 0–1 mapping) turns the raw signal into a usable map.

Outputs: image (grayscale AO/cavity map, as an image), mask (the scalar map itself - the thing you actually pack or wire onward), and ao_info (settings + the normalization range that was actually used, which is genuinely useful for matching two maps to the same range).

Inputs that matter

  • source_mode + invert_height - a dark-on-light heightmap vs. light-on-dark flips the whole result; get these right first
  • radius - neighborhood size; small gives tight crease AO, large gives broad contact shadows
  • intensity - how hard the occlusion pushes
  • output_mode - ao for general occlusion, cavity when you want the detail pass

Installing

ComfyUI Manager → search MKRShift Nodes, or:

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

Restart ComfyUI. No models, no new dependencies - this is numpy/PIL math on the CPU.

Where people get burned

  • Expecting physically-correct AO. It's an approximation. It's for lookdev speed, not for final renders where your renderer's own AO is available.
  • Flipped height - a white-on-black heightmap with invert_height off produces inverted occlusion. If your creases come out bright, flip it.
  • The mask output is the derived scalar map; the optional mask input (with mask_feather / invert_mask) only limits where the output is emitted. Pack-wise, both use the word "mask" and they are not the same thing.

Where it earns its keep: feeding AO into x1PBRPack, seeding wear masks, or checking whether a generated heightmap will hold up before you commit to a real bake. Fast, free, in-graph.

CategoryMKRShift Nodes/Surface/Tech Art

Inputs (16)

NameTypeDefaultDescription
imageIMAGE—
source_modeCOMBO7 options: luma, red, green, blue, value, alpha, +1
output_modeCOMBO2 options: ao, cavity
normalize_modeCOMBO3 options: auto_percentile, manual_range, auto_range
value_minFLOAT0.000-1–1—
value_maxFLOAT1.000-1–1—
percentile_lowFLOAT2.00–100—
percentile_highFLOAT98.00–100—
radiusFLOAT8.00–128—
intensityFLOAT2.000–8—
gammaFLOAT1.000.1–4—
invert_heightBOOLEANfalse—
mask_featherFLOAT8.00–256—
invert_maskBOOLEANfalse—
source_maskoptMASK—
maskoptMASK—

Outputs (3)

NameTypeDescription
imageIMAGE—
maskMASK—
ao_infoSTRING—