Atlas Depth Layer Mask π
The node that carves one depth band into a clean plate's masks
- solve
- depth
- exclude_mask
- band_split
- band_ref_mask
- layer_mask
- occlusion_mask
- hole_mask
- report
This is the node the layered matte-painting workflow is built around. In the classic 2.5D stack, you split a shot into depth bands and give each one its own clean plate - a piece of the background that's been painted/inpainted so the camera can move behind the foreground. AtlasDepthLayerMask produces the masks that make that possible: for one depth band, it hands you the band's own pixels, the nearer stuff that occludes it, and optionally the holes.
If that sounds like a lot of outputs, it is - three masks plus a report. But each has one clear consumer, and understanding that mapping is 90% of using the node well.
The three masks
layer_mask- the band's own pixels. "Everything in this depth slice is this layer."occlusion_mask- everything nearer than the band that hides it. This is the one that feeds the inpaint pipeline:occlusion_mask β INPAINT_ExpandMask β INPAINT_InpaintWithModelbuilds that band's clean plate. It's the whole reason this node exists.hole_mask- optional, and off by default. It comes from building the band's actual relief mesh and reading its real tear/sky holes - which is why the tooltip warns it's not free:compute_hole_mask=Trueis a real (duplicate) mesh build, unlike the other two masks. When you do turn it on, itsrelief_gridanddepth_edge_relmust match the downstreamAtlasCleanPlateLayercall, or the hole data won't reflect the final mesh.
Inputs that matter
Required: solve (the ATLAS_SOLVE) and depth (the shared ATLAS_DEPTH_MAP). Then the band definition:
near_m/far_m- the band's edges in metres.0means auto.near_pct/far_pct- used when the metres are 0. Same log-depth convention asAtlasDepthBandSplit: these are positions along the scene's log-depth range, not pixel percentiles. The tooltip is blunt about why - pixel percentiles waste 0β0.9 on the foreground. For a typical foreground object, try 0.2β0.4.feather_px(default 4) - a safety margin dilating the occlusion mask's edge.band_side+band_split- wire oneAtlasDepthBandSplitinto every band node withband_sideset (foreground/background) so the fg/bg boundary lives in exactly one widget and the layers can't drift apart. This is the recommended setup.exclude_mask- an external exclusion (a real sky segmentation from SAM/RMBG) that replaces the internal sky heuristic, so it must cover everything you want gone.band_override- a string likenear_pct=0.2 far_pct=0.5that wins over the widgets, used to flow VLM-derived band boundaries in fromAtlasAssessImage. Errors loudly on garbage.
Output: also a report that documents what the band resolved to.
How to use it
Instantiate once per background band. The occluded regions (what the foreground hides) get inpainted into that band's clean plate; the far band keeps its own layer. Priorities run farthest-first in this pack - the sky dome sits behind everything. This is the same logic the all-in-one AtlasInput node bakes in when you set layers 2β4, but here you control each band's edges yourself.
Install
Same pack, same one-liner:
cd <ComfyUI>/custom_nodes
git clone https://github.com/mikejamesvfx/atlas-camera.git
Restart (or Manager β "atlas-camera"). Needs the [neural] tier for the depth upstream (torch + GeoCalib + transformers, per INSTALL.md). Watch the kornia<0.8.3 cap so the extras don't break LTXVideo. Brand-new pack with no community folklore yet - the tooltips here are unusually long for a reason: read the ones on near_pct and compute_hole_mask before you tune.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| solve | ATLAS_SOLVE | β | |
| depth | ATLAS_DEPTH_MAP | β | |
| near_mopt | FLOAT | 0.00β10000 | Band near edge in metres. 0 = auto (use near_pct). |
| far_mopt | FLOAT | 0.00β10000 | Band far edge in metres. 0 = auto (use far_pct). |
| near_pctopt | FLOAT | 0.000β1 | Used when near_m==0: POSITION ALONG THE SCENE'S LOG-DEPTH RANGE, not a pixel percentile (depth is skewed β pixel percentiles wasted 0-0.9 on the foreground; 0.5 here = the geometric mean of the scene's depth range, perceptually mid-scene). LOWER = closer near threshold = tighter occlusion. Try 0.2-0.4 for a typical foreground object. |
| far_pctopt | FLOAT | 0.500β1 | Used when far_m==0: position along the scene's LOG-depth range (see near_pct). 0 means no upper bound (+inf); values at or above ~1.0 also mean no cap. |
| feather_pxopt | INT | 40β64 | Dilate occlusion_mask's edge by this many pixels β a small safety margin on top of whatever grow INPAINT_ExpandMask applies downstream. |
| compute_hole_maskopt | BOOLEAN | false | Build this band's own relief mesh (same as AtlasCleanPlateLayer will do later) to derive hole_mask - the mesh's real tear/sky hole data, not a depth-band heuristic. Off by default: this is a real (duplicate) mesh build, not free like the other two masks. |
| relief_gridopt | INT | 38416β4096 | Only used when compute_hole_mask=True. MUST match the AtlasCleanPlateLayer call downstream for hole_mask to reflect the actual final mesh (default 384 = the band-layer calibration). |
| depth_edge_relopt | FLOAT | 1.500.05β5 | Only used when compute_hole_mask=True. MUST match the AtlasCleanPlateLayer call downstream for hole_mask to reflect the actual final mesh (default 1.5 = the band-layer calibration). |
| exclude_maskopt | MASK | Optional external exclusion (e.g. a real sky segmentation from SAM/RMBG) which REPLACES the internal sky heuristic - so it must cover EVERYTHING you want gone. Affects layer_mask/occlusion_mask (excluded pixels can't belong to any band) AND hole_mask when compute_hole_mask=True. Any resolution - resized to match depth. | |
| fill_occludedopt | BOOLEAN | false | Only used when compute_hole_mask=True. MUST match the AtlasCleanPlateLayer setting downstream for hole_mask to reflect the actual final mesh - when the layer will diffusion-fill the occluder footprint, that footprint is no longer a hole here either. |
| band_sideopt | COMBO | manual | With band_split connected: foreground = [0, split), background = [split, +inf) β the node's own near/far widgets are ignored. manual = use this node's own near/far settings. |
| band_splitopt | ATLAS_BAND_SPLIT | Wire ONE AtlasDepthBandSplit into every band node (with band_side set) so the fg/bg boundary lives in exactly one widget and the layers can never drift apart. | |
| band_ref_maskopt | MASK | Exclusion used ONLY for resolving near/far percentages to metres. When exclude_mask carries per-layer scoping (π― scope rows), each layer's depth population differs and the shared band edges DRIFT apart (metric gaps between adjacent bands β debug-report finding). Wire the plain SKY mask here on every band node so all layers resolve identical edges. Unwired = legacy behavior (band edges from exclude_mask's population). | |
| band_overrideopt | STRING | Optional band override STRING ('near_pct=<f> far_pct=<f>') β wins over this node's near/far widgets when non-empty. Wire AtlasAssessImage's band_far/bg/mid/fg output here so the VLM's subject-aware band boundaries flow in (jointly derived, so adjacent bands always share edges exactly). Loses to a connected band_split. Errors loudly on garbage. | |
| quad_coherenceopt | BOOLEAN | true | Only used when compute_hole_mask=True. Match AtlasCleanPlateLayer to keep hole QA identical to the final mesh. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| layer_mask | MASK | β |
| occlusion_mask | MASK | β |
| hole_mask | MASK | β |
| report | STRING | β |