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 5 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