Cam Shot Toolkit: Render Offset View
The payoff node that puts your person back in the shot
- mesh_data
- reference_image
- rendered_image
- camera_info
This is the node the whole pack is named after, and the reason a 3D reconstruction is worth doing at all. Process Image gives you a mesh of a person standing in some original photo; Render Offset View lets you move the camera around that mesh and render a new frame - same person, same world, different angle - while keeping the lens calibrated to the original shot. If you've ever tried to fake a new camera angle on a 2D image with ControlNet and watched it invent geometry, this is the version that doesn't have to guess.
How it works
Two inputs set the whole thing up. mesh_data comes straight from Process Image, and reference_image is the photo the person was reconstructed from. The node reads the reconstruction's focal length and uses the reference image's aspect ratio to compute matching intrinsics - fx, fy, and the principal point - scaled to your render_width/render_height. That's the "calibrated" part: the render behaves like a real camera that was at the original shoot, so a focal_scale of 1.0 puts the mesh back in roughly the same framing the photo had.
Then it renders. Here's the part worth appreciating: there's no PyOpenGL, no pyrender, no OSMesa, no EGL - nothing to compile. The pack ships a self-contained NumPy mesh rasterizer that projects vertices, shades faces, and composites the frame. It's deliberately simple (flat-ish studio shading, mesh color, background color), but it runs anywhere your Python runs and installs without a fight. That trade - boring shading for zero native-dependency pain - is the right one for a tool whose whole point is camera setup, not final beauty lighting.
The interactive viewer
The fun part is enable_viewer. When it's on, the node mounts a Three.js preview in the browser where you can orbit, pan, roll, and dolly the camera to scout your shot. The camera you settle on gets saved into the hidden interactive_state string, and on the next workflow run the backend rasterizer reproduces that exact pose - no live WebGL frame, just the saved state. Two switches govern this:
- use_interactive_view (default on) - the browser camera overrides the parameter-based camera when saved state exists.
- auto (default off) - ignore any saved viewer camera and realign from the current SAM 3D input on every run. This is the one to flip for API/batch workflows where you don't want a stale saved camera from a previous session steering your render.
For beginners, the honest workflow is: keep use_interactive_view on, scout in the viewer, and let the saved state do the work. The parameter camera (key/lighting/bg fields) matters more once you want reproducible shots without the viewer.
The inputs you'll actually touch
Beyond the camera logic above: lighting_preset (studio/flat/dramatic), bg_preset (mid_gray/black/white/custom), mesh_r/g/b to tint the mesh, and focal_scale to push the lens wider or tighter. Render size caps at 4096. The two outputs are rendered_image (a normal IMAGE you can preview, save, or feed back into a pipeline) and camera_info, a JSON string carrying the calibrated camera - focal lengths, principal point, camera position, target - which is gold if you're exporting the shot to Blender or Unreal later.
Installation and gotchas
Same pack install as everything else here:
cd ComfyUI/custom_nodes
git clone https://github.com/EnviralDesign/sam3d-body-comfyUI-camshottoolkit
python -m pip install -r sam3d-body-comfyUI-camshottoolkit/requirements.txt
Restart, then wire mesh_data and the original photo in. The two ways people trip: forgetting reference_image, which the node needs to define intrinsics (wire the same image you processed - it's required, so this is less a trap and more a "why won't it connect" moment), and expecting a photoreal render. This is a preview/blocking render, not a final beauty pass - it gets the camera right, and you take the camera_info out to a real renderer for the pretty version.
Inputs (24)
| Name | Type | Default | Description |
|---|---|---|---|
| mesh_data | SAM3D_OUTPUT | Mesh data from SAM3DBodyProcess node | |
| reference_image | IMAGE | Reference image used to define the original camera intrinsics | |
| render_width | INT | 51264–4096 | — |
| render_height | INT | 51264–4096 | — |
| enable_viewer | BOOLEAN | true | Mount the interactive preview viewer for this node. |
| use_interactive_view | BOOLEAN | true | If enabled, the browser viewer camera overrides the parameter-based camera when available. |
| auto | BOOLEAN | false | Ignore saved viewer camera state and align from the current SAM3D input on every run. |
| show_viewer_hud | BOOLEAN | false | Show the camera HUD overlay in the interactive viewer. |
| focal_scale | FLOAT | 1.000.1–4 | — |
| lighting_preset | COMBO | studio | Quick lighting profile |
| ambient_intensity | FLOAT | 0.350–3 | — |
| key_intensity | FLOAT | 14.00–100 | — |
| key_yaw | FLOAT | 35.0-180–180 | — |
| key_pitch | FLOAT | 35.0-89–89 | — |
| fill_intensity | FLOAT | 6.00–100 | — |
| rim_intensity | FLOAT | 8.00–100 | — |
| mesh_r | INT | 2350–255 | — |
| mesh_g | INT | 2350–255 | — |
| mesh_b | INT | 2350–255 | — |
| bg_preset | COMBO | mid_gray | Quick background color preset |
| bg_r | INT | 380–255 | — |
| bg_g | INT | 380–255 | — |
| bg_b | INT | 380–255 | — |
| interactive_state | STRING | {} | Hidden interactive camera state managed by the viewer. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| rendered_image | IMAGE | — |
| camera_info | STRING | — |