Mesh Sequence Renderer
Re-shoot a whole video from a camera that was never there
- mesh_sequence
- camera_trajectory
- background_color
- rendered_video
This is the node that delivers on the pack's actual promise. VideoToMeshes gives you a pile of per-frame 3D reconstructions; MeshSequenceRenderer takes that pile and renders every mesh from the same (new) camera angle, so a video that was shot straight-on comes back out as the same motion seen from the side, from above, or from an orbit. That's the "angle shift" - a virtual re-shoot.
It's MeshRenderer's video cousin, and the wiring matches the pack's video_angle_shift.json workflow: VHS_LoadVideo → VideoToMeshes → MeshSequenceRenderer → VHS_VideoCombine, with a CameraTrajectory_T2 feeding the camera. Output goes straight back into an image batch that the video loader/combiner tools can turn into a file.
How it works
For each mesh in the sequence, the node computes the camera pose (sweeping through the trajectory's animation if animate is on - it interpolates the angle by each mesh's original frame index, so the motion and the camera move stay in sync), then calls the same nvdiffrast renderer under the hood as MeshRenderer with your chosen mode, lighting, and background. Per-mesh center and distance auto-estimation are on by default, which matters here: every reconstructed frame has slightly different geometry, and you want the camera framing the moving subject rather than drifting off it.
The outputs are stacked back into a single IMAGE batch - rendered_video - frame-aligned to the sequence's frame_indices, so downstream video nodes see a continuous video.
The inputs that matter
- mesh_sequence and camera_trajectory - required, and again these are exactly the outputs of VideoToMeshes and CameraTrajectory_T2. Nothing surprising here.
- render_mode / lighting_preset / background_color - same five modes (pbr, wireframe, cel, normal, depth) and four lighting presets as MeshRenderer. PBR + studio is the default and, for angle-shift work, usually the right call - you want the reconstruction to read as the same object, not a stylized one.
- output_width / output_height - default 1920×1080. For a re-shoot this drives both render cost and final quality; preview at 512, finish at full res.
- enable_onion_skin - the built-in shortcut. Flip it on and the renderer bakes motion ghosts in as it goes (default 3 ghosts every 5 frames, blue past / orange future). If you want control beyond that, leave it off and put the dedicated OnionSkinning node after the renderer instead.
If the trajectory has animate on, that's where the magic is: orbit gives you a moving camera circling a moving subject, dolly pushes in as the action plays. The animation range from the trajectory controls how big the move is.
Installing
Same pack setup as everywhere else:
cd ComfyUI/custom_nodes
git clone https://github.com/styletransfer/ComfyUI-TRELLIS2_Motion
cd ComfyUI-TRELLIS2_Motion
python install.py --full
pip install git+https://github.com/microsoft/TRELLIS.git
pip install git+https://github.com/NVlabs/nvdiffrast.git
Restart ComfyUI; it lives under TRELLIS2/Render.
Common issues
- "Empty mesh sequence." You wired this before running VideoToMeshes, or VideoToMeshes failed every frame (usually TRELLIS unavailable or OOM). Check the upstream node actually produced meshes - this node throws immediately on an empty list.
- Jittery or swimming results. This is the classic one with per-frame reconstruction: TRELLIS isn't temporally stable, so geometry wobbles frame to frame. Reduce it at the source - better input frames, consistent
seedin VideoToMeshes, background removal on - rather than fighting it in the renderer. For the mesh-geometry problem itself (visible triangles, "low-poly" look), the honest answer is better source images; smoothing shading is a post-process. - Camera drifts off the subject. If you set explicit
center_*in the trajectory, the renderer trusts you over auto-centering - a fixed pivot can lose a subject that moves around the scene. Leave center at defaults unless you know the scene's bounds. - It's a long run with no obvious progress. Per-frame render is cheap (~0.1–0.2s) but it's multiplied by every processed frame, and the node feeds a progress bar through ComfyUI so you can watch it count. If it stalls for many seconds per frame, drop
output_widthor trim the sequence.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| mesh_sequence | TRELLIS_MESH_SEQUENCE | Sequence of meshes from VideoToMeshes | |
| camera_trajectory | CAMERA_TRAJECTORY | Camera parameters from CameraTrajectory node | |
| output_widthopt | INT | 1920256–4096 | Output image width |
| output_heightopt | INT | 1080256–4096 | Output image height |
| render_modeopt | COMBO | pbr | Rendering style |
| background_coloropt | COLOR | #000000 | Background color |
| lighting_presetopt | COMBO | studio | Lighting configuration |
| enable_onion_skinopt | BOOLEAN | false | Enable onion skinning effect during rendering |
| onion_skin_framesopt | INT | 31–10 | Number of ghost frames |
| onion_skin_intervalopt | INT | 51–30 | Frames between ghosts |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| rendered_video | IMAGE | — |