Depth Warp 3D
The depth warp that actually matches Deforum's camera math
- image
- depth
- image
- reveal_mask
If you've tried the older ComfyUI Deforum 3D nodes and the camera felt subtly wrong - the in-plane axes mirrored, the pan rolling the wrong way - this is the node that fixes it. Depth Warp 3D is an exact forward port of original Deforum's transform_image_3d, verified pixel-identical to the real thing on all 9 camera axes by an oracle test. When the source moves right, your frame moves right, the same way the 2022 A1111-era Deforum did.
It's the camera-motion workhorse of the Stat-Imp pack: feed it a frame plus that frame's depth map, dial in translation and rotation, and out comes the warped frame - the per-frame "push" that turns a still image into a moving one inside a Deforum animation loop.
How it works
The mechanism is a forward projection, and it's pure torch - no pytorch3d, no kornia at runtime. It builds a world grid from your image (x, y in -1..1, z from the depth map), projects that grid through an old (identity) camera and a new (moved) camera, takes the screen-space offset, and resamples the image by it. The motion conventions are copied verbatim from the original's py3d_tools: row-vector transform, symmetric FoV projection, and a translate vector of [-tx, +ty, -tz] / 200. That last bit is why positive translation_x moves content right, translation_y moves it up, translation_z zooms in, and the rotations are a pan-right / tilt-up / roll-clockwise set that matches the original exactly.
Inputs that matter
imageanddepth- the frame and its depth map. The node does not estimate depth; you bring it. The pack recommendscomfyui_controlnet_aux: aMiDaS-DepthMapPreprocessor(auto-downloadsIntel/dpt-hybrid-midas) or a Depth Anything V2 preprocessor, which is generally better than the MiDaS+AdaBins pipeline the 2023 original used. Wire two preprocessors into anAnySwitchand you can swap estimators at runtime.translation_x/y/z,rotation_3d_x/y/z- the camera move, usually driven by aValueScheduleso the motion animates.fov(default 40) andtranslation_scale(default 0.005) - the two knobs that set how "wide" and how strong the move is.depth_invert- defaults to True, because MiDaS/Depth Anything output inverse depth (white = near). If your warp looks inside-out, this is the first thing to check.padding_mode/sampling_mode- edge and interpolation behavior;border/bilinearare the sane defaults.
Outputs
image- the warped frame, straight into your loop.reveal_mask- optional but genuinely useful. It marks newly-revealed (disoccluded) pixels that have no source data. Wire it intoSet Latent Noise Maskand you can inpaint only those regions - the standard fix for the smeared "holes" big camera moves leave behind. Safe to leave unconnected if your moves are gentle.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Statistical-Impossibility/comfyui-Stat-Imp-nodes
The core warp is pure torch with zero extra Python dependencies - no pytorch3d, no kornia. To run the shipped example workflow you also need the harness fork (https://github.com/Statistical-Impossibility/deforum-comfy-nodes), which widens the loop's carry slots and fixes the value-schedule parser. Restart ComfyUI, hard-refresh, and you'll find the node under Stat-Imp / Deforum / Depth3D. The depth preprocessor comes from comfyui_controlnet_aux, installed separately.
Common issues
The biggest misunderstanding is expecting depth to come free - no depth input, no warp; the node consumes a depth map, it doesn't make one. Next is polarity: with depth_invert left at its default and a non-inverse depth map, motion reads backwards, so flip the toggle and re-check. And one honest caveat from the pack's own docs: the geometry matches original Deforum exactly, but the depth values won't - the 2023 original blended MiDaS with AdaBins, and this pack leaves depth entirely to you. If your motion looks different from a vintage Deforum clip, that's almost always the depth source, not the camera math.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| depth | IMAGE | — | |
| translation_x | FLOAT | 0.00-10000–10000 | — |
| translation_y | FLOAT | 0.00-10000–10000 | — |
| translation_z | FLOAT | 0.00-10000–10000 | — |
| rotation_3d_x | FLOAT | 0.00-10000–10000 | — |
| rotation_3d_y | FLOAT | 0.00-10000–10000 | — |
| rotation_3d_z | FLOAT | 0.00-10000–10000 | — |
| fov | FLOAT | 40.01–179 | — |
| translation_scale | FLOAT | 0.00500.0001–1 | — |
| depth_invert | BOOLEAN | true | — |
| depth_equalize | BOOLEAN | true | — |
| padding_mode | COMBO | 3 options: border, reflection, zeros | |
| sampling_mode | COMBO | 3 options: bilinear, nearest, bicubic |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| reveal_mask | MASK | — |