Nodes/Atlas Camera/Atlas Export Relief Mesh (OBJ) πŸ—»
ComfyUI Node

Atlas Export Relief Mesh (OBJ) πŸ—»

The depth-following mesh as an OBJ or GLB, projection baked into the UVs

By mikejamesvfxΒ·Created 3 months agoΒ·Updated a day agoΒ· 1
Atlas Export Relief Mesh (OBJ) πŸ—»
  • solve
  • image
  • project
  • obj_path
  • glb_path
  • preview_solve
  • report
β—„output_diratlas_exportsβ–Ί
β—„grid_long_edge128β–Ί
β—„depth_edge_rel0.50β–Ί
β—„depth_modeldepth-anything/Depth-Anything-V2-Metric-Outdoor-Large-hfβ–Ί
β—„deviceautoβ–Ί
β—„formatbothβ–Ί
β—„use_solve_meshtrueβ–Ί
β—„max_edge_factor12β–Ί
β—„normal_edge_deg0β–Ί
β—„fill_interior_holesfalseβ–Ί
β—„max_hole_edges64β–Ί
β—„fill_depth_near_m0.0β–Ί
β—„fill_depth_far_m0.0β–Ί
β—„retopo_methodoffβ–Ί
β—„retopo_target_vertex_count2000β–Ί
β—„retopo_smooth_iterations0β–Ί
β—„retopo_crease_angle30β–Ί
β—„retopo_pure_quadfalseβ–Ί

AtlasExportReliefMesh is where the 2.5D projection stops being a viewport trick and becomes an actual geometry file you can hand to another tool: it writes the depth-following relief mesh as an OBJ or GLB (or both), with the plate's projection baked into the UVs. This is the "give the projection to anyone" export - Blender, Maya, game engines, a texture bake - and it's also the mesh that feeds the Nuke and Maya exporters when you want real derived geometry in those scenes instead of a flat card.

The "relief" part is the whole idea: instead of a flat plane, the mesh follows the monocular depth estimate (fed by the solved focal length, which matters - depth seen through the wrong lens is depth in the wrong place). From the recovered camera's viewpoint the plate reassembles exactly; from anywhere else you get parallax, which is precisely the illusion you're buying.

How it works

Give it the solve, the image (the plate to project), and an output_dir. It writes obj_path and glb_path (with format defaulting to both). The critical default is use_solve_mesh - on, it exports the relief mesh already on the solve (from AtlasInput or the derive nodes) so all of its edge tuning carries over. Turn it off to re-derive the mesh here instead, which unlocks the re-derive-only tuning inputs (max_edge_factor, normal_edge_deg).

A few genuinely useful options:

  • fill_interior_holes (default off) - an export-only fan-fill of small interior tear holes so the mesh retopologizes and renders cleanly, scoped by max_hole_edges and the fill_depth_near_m/fill_depth_far_m band bounds. The live viewport projection mesh is never touched.
  • retopo_method - export-only retopology (off, quad, decimate, voxel_remesh) with a vertex budget, for when the derived mesh is too messy to hand to a real DCC.
  • grid_long_edge (default 128) and depth_edge_rel - mesh density and the relative depth jump that tears the mesh.

Outputs: obj_path, glb_path, a preview_solve, and report.

Install and gotchas

Part of the pack - ComfyUI Manager β†’ search atlas-camera, or git clone https://github.com/mikejamesvfx/atlas-camera.git into custom_nodes, restart. The exporter itself is lightweight, but the mesh comes from depth: the default Depth Anything V2 backend is transformers-only (no extra install) - the heavier neural/GeoCalib path is only needed if you're re-deriving without an existing solve mesh.

Gotchas: output_dir is relative to ComfyUI's working directory, and the Nuke exporter resolves your obj_path to absolute precisely so the generated scripts stay portable - keep that convention in mind. If the exported mesh comes out "combed" or shredded on deep or interior scenes, that's the world-space edge tear threshold (max_edge_factor) tripping on grazing surfaces; raise it to 40–80, or better, tune it upstream on AtlasInput and let use_solve_mesh carry it through. And remember the OBJ is projected but not textured-on-disk - the plate rides in the UVs and the projection is applied live in the consuming tool. That's the intended pipeline, not a bug.

CategoryAtlas/10 Β· Export

Inputs (21)

NameTypeDefaultDescription
solveATLAS_SOLVEβ€”
imageIMAGEβ€”
output_dirSTRINGatlas_exportsβ€”
grid_long_edgeoptINT12816–4096Mesh density: grid columns along the longest image edge.
depth_edge_reloptFLOAT0.500.05–5Relative depth jump that tears the mesh (silhouette holes).
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
deviceoptCOMBOauto4 options: auto, cuda, mps, cpu
formatoptCOMBOboth3 options: both, obj, glb
use_solve_meshoptBOOLEANtrueExport the relief mesh ALREADY on the solve (from AtlasDeriveReliefMesh / AtlasInput) so ALL its edge tuning β€” max_edge_factor, normal_edge_deg, the band near-clip, sky_heuristic β€” carries into the OBJ/GLB exactly, with no widget to re-set. Turn OFF to re-derive from depth at this node's own grid/thresholds below (e.g. to export a HIGHER-resolution mesh than the viewport). Auto-falls-back to re-derive when the solve carries no relief mesh.
max_edge_factoroptFLOAT122–200Re-derive only (use_solve_mesh off): world-space edge tear threshold. Raise to 40-80 on deep/interior scenes to stop combs.
normal_edge_degoptFLOAT00–180Re-derive only: 0 = off; tears on surface-normal bend (real creases) while leaving smooth grazing surfaces intact.
fill_interior_holesoptBOOLEANfalseEXPORT-ONLY (the live viewport projection mesh is never touched). Fan-fill small interior tear holes in the OBJ/GLB so it retopologizes and booleans cleanly in a DCC. Fills ONLY interior enclosed boundary loops β€” never the outer silhouette/frame boundary β€” by re-using each hole's existing boundary vertices, so projection-baked UVs stay valid. Off by default: a torn silhouette is the DMP-correct look.
max_hole_edgesoptINT643–4096A boundary loop is filled only if its edge count is below this. The outer frame is ~the grid perimeter (e.g. ~512 at grid 128), interior tears are ~4-30, so 64 separates them by construction.
fill_depth_near_moptFLOAT0.00–10000Band-box spatial scope: only fill loops whose EVERY boundary vertex's forward depth (recovered-camera view space, same axis as AtlasBoundedBand's cutoff) lies within [near, far] metres. Transcribe off a bounded band's near and cutoff_m. 0 = off (edge-count-only mode; the single largest loop is always left open).
fill_depth_far_moptFLOAT0.00–100000Band-box far bound (the cutoff). 0 = off (see fill_depth_near_m).
retopo_methodoptCOMBOoffEXPORT-ONLY retopology pass on the OBJ/GLB (the live viewport projection mesh is never touched). off = no change (default, so every saved workflow keeps working). quad = pyinstantmeshes orientation-field quad remesh (cleanest DCC handoff; needs the pyinstantmeshes package). decimate = quadric decimation via fast-simplification (fewer faces, same topology class). smooth = trimesh Taubin relax (topology-preserving, UVs kept). quad/decimate change the vertex count so projection-baked UVs are REGENERATED from the recovered camera (pure numpy) and the retopologized mesh stays textured. Runs AFTER any interior hole-fill.
retopo_target_vertex_countoptINT20004–2000000Target vertex count for quad / target face count for decimate (decimate targets ~2x this in faces). Ignored by smooth.
retopo_smooth_iterationsoptINT00–100quad: Instant Meshes post-smooth iterations. smooth: Taubin relax iterations (the actual smoothing strength). decimate: ignored.
retopo_crease_angleoptFLOAT300–180quad only: crease angle (deg) below which adjacent faces are treated as one smooth surface in the orientation field.
retopo_pure_quadoptBOOLEANfalsequad only: force a pure-quad output (no triangles). False allows quad-dominant (triangles where the field can't place a quad).
projectoptATLAS_PROJECTOptional delivery project from AtlasProject β€” routes this export into the project tree's geo/ lane and supersedes output_dir.

Outputs (4)

NameTypeDescription
obj_pathSTRINGβ€”
glb_pathSTRINGβ€”
preview_solveATLAS_SOLVEβ€”
reportSTRINGβ€”