Atlas Derive Projection Geometry π½
Pick a scene type, get a projectable scene
- solve
- image
- exclude_mask
- solve
- hole_mask
- report
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, orboth.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 asAtlasDeriveReliefMesh.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.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| solve | ATLAS_SOLVE | β | |
| image | IMAGE | β | |
| depth_modelopt | COMBO | depth-anything/Depth-Anything-V2-Metric-Outdoor-Large-hf | 10 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_wallsopt | INT | 40β64 | β |
| max_objectsopt | INT | 30β32 | Max 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. |
| deviceopt | COMBO | auto | 4 options: auto, cuda, mps, cpu |
| geometry_modeopt | COMBO | relief_mesh | What 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_gridopt | INT | 12816β4096 | Viewport 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_methodopt | COMBO | azimuth_walls | azimuth_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_typeopt | COMBO | manual | The 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_qualityopt | COMBO | custom | Quick-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_relopt | FLOAT | 0.500.05β5 | Relative 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_maskopt | MASK | Optional 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_boundaryopt | BOOLEAN | false | Cut 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)
| Name | Type | Description |
|---|---|---|
| solve | ATLAS_SOLVE | β |
| hole_mask | MASK | β |
| report | STRING | β |