Nodes/WAS Node Suite v3/Image SSAO (Ambient Occlusion)
ComfyUI Node Runs on cloud

Image SSAO (Ambient Occlusion)

Fake 3D shading from a flat image and a depth map

By WASasquatch·Created 3 years ago·Updated 6 days ago· 1,844
Image SSAO (Ambient Occlusion)
  • images
  • height_maps
  • composited_images
  • ssao_images
  • specular_mask_images
strength1.00
radius30.00
height_scale64.0
ray_count16
step_count16
angle_bias0.150
ao_blur2.50
specular_threshold200
enable_specular_maskingfalse
precision32 bit float

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.

CategoryWAS Suite/Image/Filter

Inputs (12)

NameTypeDefaultDescription
imagesIMAGEThe 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_mapsIMAGEThe 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.
strengthFLOAT1.000–5How 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.
radiusFLOAT30.000.01–1024How 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_scaleFLOAT64.00–1024How 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_countINT164–64How 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_countINT161–64How 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_biasFLOAT0.1500–1How 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_blurFLOAT2.500–1024How 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_thresholdINT2000–255How 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_maskingBOOLEANfalseKeep 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.
precisionCOMBO32 bit floatHow 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)

NameTypeDescription
composited_imagesIMAGEThe source image with the shading multiplied into it, on the scale it arrived on. Light above 1.0 is dimmed rather than clipped.
ssao_imagesIMAGEThe shading on its own, as a greyscale image: white where the sky reaches, dark in the crevices.
specular_mask_imagesIMAGEThe area treated as highlight, white where it was protected from shading. Produced whether or not the masking was enabled.