Image SSAO (Ambient Occlusion)
Fake 3D shading from a flat image and a depth map
- images
- height_maps
- composited_images
- ssao_images
- specular_mask_images
Ambient occlusion is a rendering trick from the 3D/game world: it darkens the little crevices, corners, and contact points where two surfaces meet, because in real life less bounce light reaches those spots. It's why a CG render without AO looks flat and a render with it looks like it has weight. WAS Node Suite's Image SSAO brings that trick to a flat 2D image - no 3D scene required, just a depth map to fake the geometry.
The author flags it as an experimental beta node, and that's worth taking at face value. It's not a polished, battle-tested filter like High Pass or Chromatic Aberration elsewhere in this pack - it's closer to "here's a fun screen-space effect I got working," and it shows in how fiddly the results can be.
How it works
Screen Space Ambient Occlusion, as the technique's actually implemented in games, samples the depth buffer around each pixel and darkens it in proportion to how enclosed it looks - a crease between two objects reads as more occluded than an open, flat surface. This node does the same thing against a depth map instead of a real depth buffer: it needs an image plus depth information, and per the README that depth information can come from two places - the pack's own MiDaS Depth Approximation node (fed by MiDaS Model Loader, also covered in this pack) computing it for you, or you handing it a depth map you already made, say from a ControlNet depth preprocessor. Either way, out comes an image with the occlusion shading baked into it.
What you actually feed it
The two things that matter are the source image and a depth map. If you don't already have MiDaS wired up elsewhere in your graph, that's your first stop - load a MiDaS model, run Depth Approximation on your image, and pipe the result here alongside the original. If you already generate depth maps for ControlNet (most photoreal workflows do), you can often reuse that map directly instead of running MiDaS a second time.
Installing it
Grab the whole suite through ComfyUI Manager - search "WAS Node Suite" and install - or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/was-node-suite-comfyui
cd was-node-suite-comfyui
pip install -r requirements.txt
(On a portable Windows build, run path\to\ComfyUI\python_embeded\python.exe -s -m pip install -r requirements.txt instead.) Restart ComfyUI and the node shows up in the WAS Suite menu.
Where this trips people up
The pack itself hasn't been under active development since December 2023 - the author says so directly in the README, and points people who want fixes toward forking it. In practice that means the most common failure mode isn't this node specifically, it's the whole suite: after a ComfyUI core update, WAS-NS can throw "Import Failed" because it pins or downgrades packages (BLIP support being the recurring culprit) that the newer core expects at a different version. If SSAO - or any WAS node - vanishes from your menu after an update, check the console for an import error before assuming the node itself broke.
Beyond that, it's genuinely a beta filter: expect to fight with it a bit. If the occlusion looks muddy or overbaked, it's almost always the depth map, not the SSAO node - a soft, low-contrast MiDaS pass gives you soft, low-contrast occlusion. Try a sharper depth source (DPT-Large over the small MiDaS variant, or a proper ControlNet preprocessor) before assuming the effect itself is broken. And because it's screen-space rather than true 3D, don't expect it to hold up on wildly different depth ranges or extreme close-ups - it reads local depth gradients, and it'll only look convincing where those gradients are honest.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | The image to shade. A batch is handled one image at a time. Linear light is carried through unclipped, so shading a plate on its way to EXR Save keeps every highlight above 1.0. | |
| height_maps | IMAGE | The matching height map, where bright stands high and dark lies low. It is resized to the image, so it need not match its size. MiDaS Depth Approximation produces a suitable one. | |
| strength | FLOAT | 1.000–5 | How dark the shading goes. 1.0 is the measured amount, 0.5 is half as deep, 2.0 exaggerates it. 0.0 leaves the image exactly as it arrived. |
| radius | FLOAT | 30.000.01–1024 | How far each ray travels from its pixel, in pixels. 4 catches only the tight creases right at an edge; 30 gathers broad soft shading; 200 lets a distant ridge shade a whole valley. Cost does not grow with this, only with ray_count and step_count. |
| height_scale | FLOAT | 64.00–1024 | How far the map is extruded, in pixels, from black to white. This against radius is what sets the depth of the shading: 64 with a radius of 30 gives steep relief and heavy occlusion, 16 gives a gentle emboss. 0 flattens the map and the shading disappears. |
| ray_count | INT | 164–64 | How many directions are traced around the circle. 8 is fast and can band on smooth gradients, 16 is clean for most images, 32 and above for large radii where the banding shows. Cost is directly this times step_count. |
| step_count | INT | 161–64 | How many samples are taken along each ray. Too few for the radius and a narrow ridge is stepped straight over, so raise this when a large radius starts missing thin occluders. 16 suits a radius up to about 64; use 32 beyond that. |
| angle_bias | FLOAT | 0.1500–1 | How steep a ridge has to be before it shades at all, as a rise over a run. Most height maps arrive with only 256 levels, and every one of those steps is a tiny cliff that shades as concentric rings across ground that should be flat. 0.15 clears that at the default relief; raise it towards 0.3 if rings survive a larger height_scale, and drop it to 0 for a height map that came in as smooth floating point. |
| ao_blur | FLOAT | 2.500–1024 | How much the shading is softened before it is applied, in pixels. 2.5 smooths away the sampling noise; 20 turns the shading into a broad gradient. 0 applies it exactly as traced. |
| specular_threshold | INT | 2000–255 | How bright a pixel has to be, on a 0-255 scale, to count as a highlight that should not be shaded. 200 protects only genuine highlights; 25 protects everything that is not nearly black and leaves the shading doing nothing. Only read when enable_specular_masking is on, but it always decides the third output. |
| enable_specular_masking | BOOLEAN | false | Keep the bright areas picked out by specular_threshold free of shading. On protects highlights and light sources from being darkened; off shades the whole image from its relief alone. |
| precision | COMBO | 32 bit float | How finely the three outputs are stepped, measured on the 0 to 1 scale. '32 bit float' keeps every value and is what EXR Save and DNG Save want; '16 bit' rounds to steps of 1/65535, still smooth enough for a graded plate; '8 bit' rounds to steps of 1/255, which bands a soft gradient and only matches what a PNG can hold anyway. Nothing is clipped at any setting: linear light above 1.0 keeps its value and lands on the same ladder of steps, so a highlight at 4.0 has four times as many steps under it as one at 1.0. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| composited_images | IMAGE | The source image with the shading multiplied into it, on the scale it arrived on. Light above 1.0 is dimmed rather than clipped. |
| ssao_images | IMAGE | The shading on its own, as a greyscale image: white where the sky reaches, dark in the crevices. |
| specular_mask_images | IMAGE | The area treated as highlight, white where it was protected from shading. Produced whether or not the masking was enabled. |