Generate AO Map
Real ambient occlusion from a height map, computed not faked
- height_map
- IMAGE
Lots of "AO" you see in texture pipelines is a darkening gradient slapped on for looks. Generate AO Map is not that. It does actual height-field ambient occlusion: it takes your height map, casts many sample rays per pixel across the height field, and computes where the surface would block the light. The result is a real AO mask you can trust - baked from geometry, not guessed.
It's the centerpiece of this pack's PBR generation story. Feed it a height map (DeepBump Normal to Height and the pack's height tools are right there to produce one), and out comes an AO map that'll sit under your albedo in-engine and ground the lighting.
How it works
The height field is treated as a grayscale surface (white high, black low), and for each pixel the node samples num_samples points within a radius, checks the local height differences, and accumulates occlusion with falloff. It's fully vectorized PyTorch using grid_sample, and it runs on CUDA automatically when ComfyUI's PyTorch has a GPU available, falling back to CPU.
The inputs that matter:
height_map- grayscale, white = high.auto_levels(default on) - stretches the height map to full 0–1 range before processing. Leave it on unless your map is already normalized.radius_percent(default 3) - the AO sampling radius as a percentage of image size, so it scales with resolution. Bigger radius = broader, softer ambient shadows.strength(default 2.5) - how dark the shadows get. Higher = darker.contrast(default 1) - the falloff curve; higher gives sharper shadow edges, lower gives soft gradients.bias(default 0.001) - the anti-"acne" offset that prevents self-shadowing speckle on flat surfaces. Lower = more detail but more noise.num_samples(default 24) - samples per pixel. 16–32 is the sweet spot; higher is smoother and slower.blur_radius(default 1.5) - a final Gaussian blur to clean up noise. 0 disables it.
Output and use
One IMAGE, grayscale AO. It feeds straight into a DDS Saver (BC4_UNORM, Linear) or sits alongside your other PBR maps in a material chain.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/lilquail/ComfyUI-Leputen-Utils
Restart, find it under Leputen-Utils → PBR. It uses ComfyUI's existing PyTorch - no extra model downloads, no ONNX runtime, nothing to clone. That's the good news about this node: it's compute, not ML.
Troubleshooting
- Speckle/acne on flat areas - raise
bias. It's the tradeoff knob: too high and you lose shallow detail, too low and flat surfaces sparkle. - Noisy result - bump
num_samplesand/orblur_radius. 24 samples plus a light blur handles most textures. - AO too strong/weak -
strengthis the master volume;contrastshapes the edges. Don't fight either with the others set to defaults first. - CPU slog at high res - AO is O(samples × radius) per pixel; on CPU a 2K map with 64 samples will take a while. It's CUDA-accelerated when available, which is most of the time on a real ComfyUI rig.
The pleasant surprise of this node is that it's predictable. You can reason about what radius_percent and strength do, adjust them, and the output does what you expect - unlike the AI-derived maps in the pack, which are a bit more "vibes."
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| height_map | IMAGE | The input grayscale height map (white is high, black is low). | |
| auto_levels | BOOLEAN | true | Automatically stretch the height map's contrast to use the full 0-1 range before processing. |
| radius_percent | FLOAT | 3.00.1–20 | Sample radius as percentage of image size. Scales automatically with resolution. |
| strength | FLOAT | 2.50–10 | Controls the intensity of the AO effect. Higher values result in darker shadows. |
| contrast | FLOAT | 1.00.1–4 | Adjusts the falloff curve. Higher = sharper shadows, Lower = softer gradients. |
| bias | FLOAT | 0.0010–0.1 | Helps prevent 'acne' artifacts on flat surfaces. Lower = more detail but more noise. |
| num_samples | INT | 244–128 | Number of samples per pixel. Higher = smoother but slower. 16-32 is usually sufficient. |
| blur_radius | FLOAT | 1.50–50 | Final Gaussian blur to smooth the result. Set to 0 to disable. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |