Nodes/Atlas Camera/Atlas Fill Occluded πŸ•³οΈβ€πŸ©Ή
ComfyUI Node

Atlas Fill Occluded πŸ•³οΈβ€πŸ©Ή

Fill the holes a camera move reveals β€” and register each fill back as geometry

By mikejamesvfxΒ·Created 2 months agoΒ·Updated a day agoΒ· 1
Atlas Fill Occluded πŸ•³οΈβ€πŸ©Ή
  • solve
  • source_image
  • model
  • clip
  • vae
  • camera_path
  • primary_depth
  • exclude_mask
  • solve
  • image
  • report
β—„exact_view_overrideβ–Ί
β—„max_rois8β–Ί
β—„min_area_px1024β–Ί
β—„pad_frac0.10β–Ί
β—„snap64β–Ί
β—„hole_dilate_px4β–Ί
β—„max_gen_long_edge1280β–Ί
β—„promptReconstruct the masked region so it matches the surrounding lighting direction, materials, palette and camera perspective. The masked pixels are geometry the original camera never saw, revealed by a camera move -- continue the surrounding surfaces, do not invent new objects.β–Ί
β—„seed990112233β–Ί
β—„fill_modelβ–Ύβ–Ί
β—„flux_guidance30.0β–Ί
β—„steps20β–Ί
β—„cfg1.0β–Ί
β—„sampler_nameβ–Ύβ–Ί
β—„schedulernormalβ–Ί
β—„denoise1.00β–Ί
β—„relief_grid96β–Ί
β—„camera_sourceβ–Ύβ–Ί
β—„on_registration_failureβ–Ύβ–Ί
β—„registration_min_inliers40β–Ί
β—„registration_max_residual_m0.35β–Ί
β—„registration_max_deviation_deg25.0β–Ί
β—„min_gen_long_edge1024β–Ί

The moment your camera moves off the recovered viewpoint, it reveals parts of the scene the photograph never saw - the far side of a column, the ground behind a foreground figure. Those are disocclusion holes, and they're the thing that makes a 2.5D projection read as a cheap card trick instead of a move. Most fill approaches answer the wrong question: they fill a hole in a render, which is only valid for the exact camera path it was generated against. Change the angle by a degree and the fill is worthless, and nothing ever comes back into the scene.

Atlas Fill Occluded πŸ•³οΈπŸ©Ή fills a hole in the scene. Each fill is registered back as a ProjectionSource - real geometry the solve owns - so it exports to your DCC and every later camera move reuses it instead of re-inventing it. That's the whole architectural difference, and it's why this node sits in Atlas/advanced and why the fix it does actually sticks.

How it works

For each move-revealed cluster of holes it runs a per-ROI loop: survey the hole regions β†’ crop the ROI at its own native raster β†’ fill it with an edit model at that size β†’ composite the fill back into the plate β†’ register the fill plus its crop camera back as geometry via AtlasAddPatchView. It fills at most max_rois clusters (default 8), largest first, with min_area_px (default 1024) as the floor. That budget is on purpose: leftover speckle at the rim of a tear is cheaper handled by a band layer than by a diffusion pass each. Residual hole after a defaults run is expected - it isn't evidence the node failed.

The fill model defaults to flux_fill, wired through InpaintModelConditioning so the noise mask confines the sampler to the hole. qwen_edit is the alternative (Qwen Image Edit with an inpainting LoRA is the author's measured pairing for registering fills). If you pick flux_fill, remember its guidance wants to be high - the default flux_guidance of 30 is there because Fill models are not ordinary FLUX. The node takes model, clip, vae sockets like any loader-based graph, and fill_model is not validated against what you wire in - matching them is on you.

What matters most

  • camera_path - the move whose disocclusion you're filling. Holes are surveyed at its final frame, whose holes are a superset of every earlier frame's. No path = the solved pose, which by definition has almost nothing to fill.
  • primary_depth - metric depth of the primary scene. The register_to_primary camera mode requires it; without it every ROI falls to the failure policy.
  • prompt - a sensible default ("continue the surrounding surfaces, do not invent new objects") that reads like it was written by someone who's seen a fill model wallpaper a hole with a beach. min_gen_long_edge exists for the same reason: below ~1024, small clusters reach FLUX at native size and answer with nonsense - the author measured two 256Γ—256 ROIs both answered with sand.

Outputs are the repaired image, the updated solve, and a report. Read the report's fillable hole numbers, not the raw peak hole figure - the raw number counts sky and off-plate pixels and will make a good run look like a failure.

Installing

Part of atlas-camera:

cd <ComfyUI>/custom_nodes
git clone https://github.com/mikejamesvfx/atlas-camera.git

Restart. The heavy lifting is model loading, which you already know how to do - you need FLUX Fill (or Qwen Image Edit) plus CLIP and VAE on the inputs. No non-commercial model is required by the pack itself.

Honest expectations

This is the most demanding node of the batch, and the author's own notes are refreshingly blunt about what "worked" means: at defaults on their test shot, fillable hole went from 4.56% to 1.28% of frame - 72% of the real disocclusion closed, zero pixels opened. That's the shape of success: not zero holes, but most of the real ones gone with none invented. And the default camera_source (declared_orbit) constructs the end pose exactly rather than measuring it, because a measured pose of a generated view was demonstrably worse than the constructed one. If you switch to register_to_primary, on_registration_failure decides what happens to ROIs the registration won't stand behind - skip for unattended loops, declared_orbit for artist-mode fallback. For a first run, leave the defaults alone and read the report.

CategoryAtlas/advanced

Inputs (31)

NameTypeDefaultDescription
solveATLAS_SOLVEβ€”
source_imageIMAGEβ€”
modelMODELThe edit model that fills each crop -- Qwen Image Edit 2509 with an inpainting LoRA is the measured pairing. It must be an EDIT model: a video model's fills cannot be registered.
clipCLIPβ€”
vaeVAEβ€”
camera_pathoptATLAS_CAMERA_PATHThe move whose disocclusion is being filled. Holes are surveyed at its FINAL frame, whose holes are a superset of every earlier frame's. No path = the solved pose, which by definition has almost nothing to fill.
primary_depthoptATLAS_DEPTH_MAPMetric depth of the primary. REQUIRED by register_to_primary -- without it every ROI falls to the failure policy.
exclude_maskoptMASKRegion carried by something other than geometry (a SkyDome, a matte). Subtracted from every survey mask, so it can only ever REMOVE candidate clusters.
exact_view_overrideoptSTRINGAtlasCameraMovePreset's exact_view. Carries the pivot= term, so the patch camera reproduces the move's END pose exactly instead of orbiting the ground ray.
max_roisoptINT81–64Ceiling on clusters filled, largest first. The long tail of speckle is not worth a generation each.
min_area_pxoptINT102464–1048576β€”
pad_fracoptFLOAT0.100–0.5β€”
snapoptINT648–128β€”
hole_dilate_pxoptINT40–64β€”
max_gen_long_edgeoptINT1280256–4096β€”
promptoptSTRINGReconstruct the masked region so it matches the surrounding lighting direction, materials, palette and camera perspective. The masked pixels are geometry the original camera never saw, revealed by a camera move -- continue the surrounding surfaces, do not invent new objects.β€”
seedoptINT9901122330–72057594037927940β€”
fill_modeloptCOMBOflux_fill (default) wires FLUX Fill through InpaintModelConditioning, whose noise_mask confines the sampler to the hole. qwen_edit wires Qwen Image Edit, whose mask is advisory -- measured, it returned the hole's neutral fill untouched even with 79% of the crop as real context. Wire the loaders to match: the model/clip/vae inputs are not validated against this choice.
flux_guidanceoptFLOAT30.00–100FLUX Fill's guidance (ignored by qwen_edit). The Fill models want a much higher value than ordinary FLUX.
stepsoptINT201–200β€”
cfgoptFLOAT1.00–30β€”
sampler_nameoptCOMBO6 options: euler, euler_ancestral, dpmpp_2m, dpmpp_2m_sde, ddim, uni_pc
scheduleroptCOMBOnormal6 options: beta57, normal, karras, simple, beta, sgm_uniform
denoiseoptFLOAT1.000–1β€”
relief_gridoptINT9616–512β€”
camera_sourceoptCOMBOdeclared_orbit (default) uses the camera Atlas CONSTRUCTED for this fill -- the move's end pose, exact, not a guess. register_to_primary MEASURES it instead, which is right for a novel-view generator whose camera is unknown and wrong here: measured 2026-09-03, the composited frame registers with 3583 inliers yet lands 10.1 deg from the pose it was demonstrably rendered at, while the plate at the primary pose -- a genuine 12 deg away -- measured 10.0 deg. The estimate does not track the difference, so a high inlier count is not proof of a correct pose.
on_registration_failureoptCOMBOAn unattended loop must not append geometry the pixels do not support. 'skip' drops the ROI and reports it; 'declared_orbit' restores AtlasAddPatchView's artist-mode fallback.
registration_min_inliersoptINT404–10000β€”
registration_max_residual_moptFLOAT0.350.01–10β€”
registration_max_deviation_degoptFLOAT25.01–180β€”
min_gen_long_edgeoptINT10240–4096Scale each crop UP to this long edge before the fill, and back DOWN to its native rect afterwards (0 = off). max_gen_long_edge only caps, so without a floor a small cluster reaches the model at its native size -- and an auto survey produces exactly those. Measured 2026-09-04 on the sea-cliff castle: two 256x256 ROIs, and FLUX Fill answered both with a sand beach. 1024 is FLUX's own training band; the cap still wins a conflict. Nothing downstream sees the working raster -- the fill is resampled to the crop rect before the composite and before the patch camera.

Outputs (3)

NameTypeDescription
solveATLAS_SOLVEβ€”
imageIMAGEβ€”
reportSTRINGβ€”