Nodes/BrainDead Nodes/BD Depth To Shadow Map
ComfyUI Node

BD Depth To Shadow Map

Fake believable lighting from a depth image

By BizaNator·Created 8 months ago·Updated 3 days ago· 15
BD Depth To Shadow Map
  • depth
  • mask
  • shadow_map
  • normal_map
  • cavity_map
  • measured_max_curvature
light_x0.00
light_y-1.00
light_strength0.70
ao_strength0.60
ao_radius8
ambient0.35
depth_blur2.0
invert_depthfalse

A depth map is boring until you realize it's secretly a surface normal map waiting to happen. BD Depth to Shadow Map does exactly that conversion: feed it a depth image and it computes per-pixel lighting - top-down N·L shading plus cavity-based ambient occlusion - and spits out a greyscale "shadow map" that looks like someone actually lit your character. It's a procedural shader input, not a measurement, and for the job it's built for - feeding a skin shader's shadow channel or a PBR derivation pipeline - that's exactly right.

It was designed for the multi-tone skin pipeline in this pack (there's a zombie variant that cranks the AO for sunken eyes and hollow cheeks), but it works anywhere you have depth and want a lighting guide: game-UV prep, stylized shading, "fake a light rig" on a single image.

How the magic happens

Three ingredients get blended per pixel:

  1. N·L lighting - it derives a surface normal from the depth gradients and lights it with a direction you control. Default is top-down (light_y = -1), which is why the forehead and brow ridge catch light while sockets fall into shade.
  2. Cavity AO - the Laplacian of the depth, which is a fancy way of saying "recesses get dark." Eye sockets, the mouth crease, cheek hollows. ao_radius (8–16 px default) controls what size of hollow counts.
  3. Ambient floor (ambient, 0.35) - guarantees nothing goes pure black, so the result stays usable as a multiplier.

light_strength and ao_strength mix those first two (both can go >1 for exaggeration), depth_blur smooths noisy depth before normals are computed, and invert_depth exists because estimators disagree on the "brighter = closer" convention - if your shadows look inverted, flip it.

Inputs: depth (IMAGE, R channel used if RGB; brighter = closer, unless you invert) and an optional mask - pixels outside the mask just get the ambient floor, so a skin mask keeps the shadow map from doing anything weird to the background.

Outputs: shadow_map (the greyscale multiplier - wire it into u_image3.R in the skin shader or any luma-based shadow slot), plus normal_map, cavity_map (handy for debugging what the AO is seeing), and measured_max_curvature (a float for tuning).

Installing

It ships in ComfyUI-BrainDead, so: ComfyUI Manager → search "BrainDead" → install, or

cd ComfyUI/custom_nodes
git clone https://github.com/BizaNator/ComfyUI-BrainDead
cd ComfyUI-BrainDead
pip install -r requirements.txt

then restart. No model downloads - this is pure numpy/torch math on whatever depth you feed it. Upstream, BD Lotus-2 Predict gives you the good diffusion-based depth; DepthAnythingV2 or MiDaS from comfyui_controlnet_aux work fine too if the quality bar is lower.

Where people get burned

The depth convention. If your shadows look "scooped out" backwards, invert_depth is the first toggle, not light_y. Second: this is a guide, not ground truth - the output is meant to be multiplied into a shader channel and then tuned by eye, so don't be surprised that ao_strength at 0.6 looks flat and 1.2 looks like a zombie. Both are intended outcomes.

Category🧠BrainDead/Segmentation

Inputs (10)

NameTypeDefaultDescription
depthIMAGEDepth map image. Brighter = closer to camera. Typically from Lotus2 or a depth estimator. If RGB, the R channel is used.
light_xFLOAT0.00-1–1Light direction X (image-space). -1=left, +1=right, 0=centered.
light_yFLOAT-1.00-1–1Light direction Y (image-space). -1=TOP (default, top-down lighting), +1=bottom, 0=horizontal.
light_strengthFLOAT0.700–2How much N·L contributes to final shadow. 0=disable top-light, 1=full Lambertian, >1=exaggerated.
ao_strengthFLOAT0.600–2How much cavity AO contributes. 0=disable, 1=normal, >1=exaggerated darkness in recesses (more zombie). This is what creates the sunken-eye / hollow-cheek look.
ao_radiusINT81–64Pixel radius for cavity detection (Laplacian sigma). 8-16 catches eye sockets / cheek hollows. Smaller = fine creases only. Larger = broad hollows.
ambientFLOAT0.350–1Minimum output value (ambient floor). Prevents pure-black voids. 0.35 = output never goes below 35% grey. Higher = brighter overall.
depth_blurFLOAT2.00–16Pre-blur applied to depth before normal computation. Higher = smoother lighting (less noisy). 0 = raw depth. Helps if depth has stair-step quantization or noise.
invert_depthBOOLEANfalseON: treat darker pixels as closer (Lotus2 convention is the opposite). Toggle if shadows appear inverted (sockets lit, peaks dark).
maskoptMASKOptional mask. Pixels outside the mask get the ambient floor value (not the computed shadow). Use a skin mask to limit AO to face.

Outputs (4)

NameTypeDescription
shadow_mapIMAGEGreyscale shadow map (R=G=B). 1.0=fully lit, 0.0=fully shadowed (clamped by ambient floor). Wire to u_image3.R input (or use as a shadow_mult multiplier in any pipeline).
normal_mapIMAGEDerived surface normal visualized as RGB. R=nx, G=ny, B=nz, encoded [-1,+1] → [0,1] (standard tangent-space normal map). Useful for debugging or downstream lighting nodes.
cavity_mapIMAGECavity AO term in isolation (R=G=B). 1.0=open surface, 0.0=deepest cavity. Useful for visualizing what AO is detecting.
measured_max_curvatureFLOATPeak Laplacian magnitude in the depth — useful for tuning ao_strength across characters with different depth scales.