Nodes/Atlas Camera/Atlas Derive Projection Geometry πŸ“½
ComfyUI Node

Atlas Derive Projection Geometry πŸ“½

Pick a scene type, get a projectable scene

By mikejamesvfxΒ·Created 3 months agoΒ·Updated a day agoΒ· 1
Atlas Derive Projection Geometry πŸ“½
  • solve
  • image
  • exclude_mask
  • solve
  • hole_mask
  • report
β—„depth_modeldepth-anything/Depth-Anything-V2-Metric-Outdoor-Large-hfβ–Ί
β—„max_walls4β–Ί
β—„max_objects3β–Ί
β—„deviceautoβ–Ί
β—„geometry_moderelief_meshβ–Ί
β—„relief_grid128β–Ί
β—„primitive_methodazimuth_wallsβ–Ί
β—„scene_typemanualβ–Ί
β—„relief_qualitycustomβ–Ί
β—„depth_edge_rel0.50β–Ί
β—„sub_quad_boundaryfalseβ–Ί

If AtlasDeriveReliefMesh is the specialist that builds one great depth mesh, AtlasDeriveProjectionGeometry is the generalist that builds whatever your shot needs - a relief mesh, fitted primitives, or both - and it's the node that feeds the viewport's πŸ“½ Project mode. It's where the pack's "composable geometry" strategy lives: you derive what fits your scene type, not a one-size mesh.

The design is a big deal for beginners because it inverts the usual "tune five widgets" problem. Most of this node's widgets silently don't matter depending on your scene - the tooltip on primitive_method says outright that it's ignored whenever geometry_mode=relief_mesh. So instead of learning which knobs do what, you pick a scene_type preset, and the node picks a complete, self-consistent combination of every relevant widget for you.

The input that matters most: scene_type

Default manual leaves everything exactly as set (fully backward compatible with older workflows). The presets are named for the shot, not the algorithm:

  • organic - smooth relief mesh, general cluttered/natural scenes.
  • mountains - relief at high density; ridgelines need the extra grid to read as continuous rather than faceted.
  • forests - high density with a relaxed tear threshold, so noisy canopy doesn't shred into swiss cheese.
  • aerial - relief and primitives together, for the drone/top-down case where buildings read as boxes on the ground.
  • indoor - primitives + room_cuboid + the Indoor depth model.
  • outdoor - primitives + ransac_planes + Outdoor depth model (sloped roofs, stepped facades).
  • simple_walls - primitives + azimuth_walls, fast flat-wall blockout.
  • towers_spires - primitives + vertical_extrusion, for tall/sloped silhouettes.

When anything but manual is set, the widgets below grey out and show the values the preset is using. There's a matching convenience preset, relief_quality (low/medium/high/ultra), which only sets relief_grid - quick density picks without touching the rest.

Other inputs worth knowing

Required: solve and image. Then:

  • geometry_mode - relief_mesh (recommended default), primitives, or both.
  • max_walls (default 4) - the wall/plane budget. max_objects (default 3) - foreground boxes/cylinders; the tooltip's street-level advice is memorable: try 0, because 2D occupancy clustering merges cars/fences/trees into oversized near-camera boxes that dominate any orbit.
  • depth_model - which depth backend to run (the presets override this per scene).
  • depth_edge_rel (default 0.5) - the relief-mesh tear threshold, same parameter as AtlasDeriveReliefMesh.
  • exclude_mask - replaces the sky heuristic; only affects the relief branch.
  • sub_quad_boundary - cut torn cells at the depth cliff (measured 5.67β†’1.43px boundary error). Enable when silhouettes matter.

Outputs: solve (now carrying proxy_geometry - a deep copy, so your input solve isn't mutated), hole_mask, and report.

The one real gotcha

Solve JSON exports grow by ~1MB when a mesh is attached, and higher relief quality means a bigger browser viewport payload. Fine for single shots; annoying if you're exporting solve JSON on a loop. And note the preset's reach has a limit: if your solve node uses height_mode=measure_from_depth, the tooltip tells you to match its depth model to this node's by hand - the preset only reaches this node's depth estimate.

Install

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

Restart (or Manager β†’ "atlas-camera"). Needs [neural] (torch + GeoCalib + transformers; GeoCalib GitHub-only). The kornia<0.8.3 cap protects LTXVideo; portable ComfyUI should use --no-deps for GeoCalib. New pack, no community war stories - the presets and tooltips are the docs to trust.

CategoryAtlas/05 Β· Geometry

Inputs (14)

NameTypeDefaultDescription
solveATLAS_SOLVEβ€”
imageIMAGEβ€”
depth_modeloptCOMBOdepth-anything/Depth-Anything-V2-Metric-Outdoor-Large-hf10 options: depth-anything/Depth-Anything-V2-Metric-Outdoor-Large-hf, depth-anything/Depth-Anything-V2-Metric-Indoor-Large-hf, depth-anything/DA3METRIC-LARGE, depth-anything/DA3MONO-LARGE, depth-anything/DA3NESTED-GIANT-LARGE-1.1, Ruicheng/moge-2-vitl-normal, +4
max_wallsoptINT40–64β€”
max_objectsoptINT30–32Max foreground boxes/cylinders. Street-level scenes: try 0 β€” the 2D occupancy clustering merges cars/fences/trees into oversized near-camera boxes that dominate any orbit.
deviceoptCOMBOauto4 options: auto, cuda, mps, cpu
geometry_modeoptCOMBOrelief_meshWhat the viewport receives. relief_mesh = contoured depth mesh (recommended); primitives = flat blockout planes/boxes; both overlaps the two on the same surfaces (enclosure + z-shimmer).
relief_gridoptINT12816–4096Viewport relief-mesh density (long-edge grid columns). Higher = fewer/smaller torn holes on noisy AI-image depth (each quad spans less real-world area, so it's less likely to straddle a spurious depth jump) at the cost of a larger mesh payload sent to the browser and a slower/heavier viewport. Overridden by relief_quality unless that's set to 'custom'.
primitive_methodoptCOMBOazimuth_wallsazimuth_walls (default) = vertical walls only, height clipped to the plain wall (truncates sloped roofs/spires/towers). ransac_planes = any-orientation planes (roofs, stepped facades) β€” exteriors. room_cuboid = Manhattan floor+walls+ceiling β€” orthogonal interiors. vertical_extrusion = same wall orientation as azimuth_walls but height extruded to the real image-space silhouette top (reaches towers/spires/sloped roofs azimuth_walls truncates). Only affects geometry_mode=primitives/both; max_walls is reused as the plane budget for ransac_planes and ignored by room_cuboid. Ignored when scene_type != manual.
scene_typeoptCOMBOmanualThe one choice that matters β€” picks a complete, self-consistent combination of geometry_mode/primitive_method/relief_quality/depth_edge_rel/max_objects/depth_model for a named shot type, so you never have to know which of those five widgets actually does anything for your scene (e.g. primitive_method is silently ignored whenever geometry_mode=relief_mesh β€” this picks a combination where that can't happen). When this is anything but 'manual', the widgets below it grey out and show the values this preset is using. organic = smooth relief mesh, general-purpose natural/cluttered scenes. mountains = relief mesh at high density (terrain/ridgelines need more grid resolution than the default to read as continuous rather than faceted). forests = relief mesh at high density with a relaxed tear threshold β€” dense canopy depth is genuinely noisy at a small scale, so the default threshold shreds it into holes; this trades a little silhouette accuracy for a filled-in canopy instead of swiss cheese. aerial = relief mesh AND primitives together (geometry_mode=both) with more foreground objects allowed β€” buildings read as boxes sitting on/above the relief-mesh ground and treeline, the drone/top-down shot case. indoor = primitives + room_cuboid + the Indoor depth model (orthogonal interiors). outdoor = primitives + ransac_planes + the Outdoor depth model (sloped roofs, stepped facades). simple_walls = primitives + azimuth_walls (fast flat-wall blockout, general exteriors). towers_spires = primitives + vertical_extrusion (reaches tall/sloped silhouettes azimuth_walls truncates). manual (default) leaves every widget below exactly as set β€” fully backward compatible with workflows saved before this widget existed. If AtlasLearnedSolveFromImage's height_mode=measure_from_depth, set its own depth_model to match by hand β€” this preset only reaches this node's depth estimation, not the upstream solve node's.
relief_qualityoptCOMBOcustomQuick-pick override for relief_grid: low=64, medium=256, high=512, ultra=1024. 'custom' (default) leaves relief_grid exactly as set above β€” fully backward compatible. Same convenience-preset pattern as scene_type: this only sets relief_grid, no new solving path. 'ultra' produces a much larger mesh β€” expect a slower viewport and bigger solve JSON exports.
depth_edge_reloptFLOAT0.500.05–5Relative depth jump that tears the mesh into a silhouette hole. Lower = tears more readily (cleaner silhouettes, more holes on noisy depth); higher = tears less (fewer holes, more risk of rubber-sheeting a real silhouette onto the background). Same parameter and default as AtlasExportReliefMesh.
exclude_maskoptMASKOptional external exclusion (e.g. a real sky segmentation from SAM/RMBG) which REPLACES the internal sky heuristic before triangulation - so it must cover EVERYTHING you want gone. Only affects the relief_mesh branch (geometry_mode both/relief_mesh); the primitives/wall-fitting branch is unaffected. Any resolution - resized to match depth.
sub_quad_boundaryoptBOOLEANfalseCut a torn cell AT the depth cliff instead of deleting the whole cell. Tearing is per grid cell, so a silhouette can only turn in whole-cell steps AND a cell of real surface is lost on both sides of every cliff - measured 5.67px mean boundary error at grid 128 on a 1024px plate (step 8px), i.e. WORSE than the 4px quantization bound. This finds the cliff in the full-resolution depth and rebuilds each side up to it, never joining them: 5.67 -> 1.43px, and 1.35px with boundary_smooth_iterations on top. Costs ~5% more vertices (it scales with silhouette LENGTH, not mesh area). The tear itself is untouched - same thresholds, same cells torn.

Outputs (3)

NameTypeDescription
solveATLAS_SOLVEβ€”
hole_maskMASKβ€”
reportSTRINGβ€”