Gaussian Shot Render
Frame a camera in a 3D splat scene and render it to a plain image
- extrinsics
- intrinsics
- image
Gaussian Shot Render is the "press the shutter" node of the gaussian-splat-shot-render pack. Feed it a Gaussian Splatting PLY scene and it renders a camera shot - yaw, pitch, roll, distance around a pivot - and hands you a completely ordinary IMAGE on the other end. That's the whole point: it's not a 3D tool that happens to live in ComfyUI, it's a way to turn a 3D scene into images you can push through img2img, inpainting, ControlNet or upscaling like any other tensor.
The fun part is the interactive WebGL scout viewer that lives inside the node. You orbit, dolly and tilt in real time in the browser, and the actual raster stays aligned with what you're looking at. The node is one half of a two-shot workflow this pack ships: point it at a PLY you already have, or pair it with the pack's SHARP nodes (Load SHARP Model + SHARP Predict) to go from a single photo straight to a splat scene, then start framing shots. Single image → 3D → cinematic camera moves, all in the graph.
How it works
This is where you need to calibrate expectations. The inline viewer is WebGL (gsplat, vendored in the repo so you don't need any extra 3D pack) running on your GPU, and it's smooth. The final raster - the thing that actually becomes your image - is a CPU splatter written in NumPy, optionally Numba-JIT compiled. Preview and capture are completely different code paths. Realtime preview absolutely does not imply a fast render. When you hit "run," the node decodes the PLY, builds a camera state from your pivot and angle inputs (or from SHARP's extrinsics/intrinsics if you've connected them), splats the in-frustum gaussians onto a canvas, and returns the image plus a JSON blob of camera state for the viewer to sync with.
The inputs that actually matter
Most of the widget list is camera math you can poke from the viewer and let sync back, but a few you'll set by hand:
ply_path- the PLY file. It's marked optional in the schema because it can be wired in, but there's no render without it; the node throws if it's missing.extrinsics/intrinsics- SHARP's camera metadata. Connect them anduse_source_resolutionmakes the output match the source photo's size, ignoringoutput_width/output_height.output_width/output_height- raster resolution (64–4096); the viewer matches the aspect.camera_locked- commit to the procedural output camera instead of following your interactive preview.seedplus therand_*min/max pairs - jitter translation, yaw, pitch, roll per seed. Handy for generating a batch of subtly different "takes" off one framing.max_gaussians(0 = uncapped, or top-N by importance),gaussian_scale,background(black / mid-gray / white).
Output is a single image (IMAGE tensor). Wire it straight into your downstream nodes.
Installing it
Grab it through ComfyUI Manager (search "Gaussian Splat Shot Render"), or the usual way:
cd ComfyUI/custom_nodes
git clone https://github.com/EnviralDesign/gaussian-splat-shot-render-comfyui
pip install -r gaussian-splat-shot-render-comfyui/requirements.txt
Then restart ComfyUI and refresh the browser. The requirements are light - numpy, plyfile, huggingface-hub - plus numba, which is listed as strongly recommended rather than mandatory. No model download for the render node itself; you just need a PLY. (The SHARP path pulls a checkpoint from Hugging Face on first use.)
Where people get burned
- The raster is CPU. Install numba or a 1024² render of a dense splat will feel like a long bath. The first splat in a session may pause while LLVM compiles; later runs are fast. Disk cache is off by default - set
GAUSSIAN_SHOT_NUMBA_DISK_CACHE=1on a stable machine to keep the compile. - VRAM confusion. The node never runs the splat on CUDA. GPU memory you see afterwards is SHARP, preview caches, or the browser's WebGL tab, not this node.
- RAM creep. One decoded PLY is cached in-process (path + mtime keyed), which is great for repeat runs and heavy on big clouds.
GAUSSIAN_SHOT_DISABLE_PLY_CACHE=1trades I/O for memory if it bites. - License. The original code is MIT, but the bundled WebGL viewer is GPL-3.0-or-later (from
comfy-3d-viewers) - fine for personal use, just know it's in the third-party notices.
Compared to a full 3D viewport pack like Action Director, this is a scalpel: it doesn't animate rigs or emit depth/normal passes, it frames shots and bakes them to images. If that's the job, it does it without dragging a whole 3D pipeline into your graph.
Inputs (37)
| Name | Type | Default | Description |
|---|---|---|---|
| camera_locked | BOOLEAN | false | — |
| pivot_x | FLOAT | 0.0000-1000000–1000000 | Orbit / look-at pivot in scene units (PLY space). |
| pivot_y | FLOAT | 0.0000-1000000–1000000 | — |
| pivot_z | FLOAT | 0.0000-1000000–1000000 | — |
| cam_yaw_deg | FLOAT | 0.00-1000000–1000000 | — |
| cam_pitch_deg | FLOAT | 0.00-1000000–1000000 | — |
| cam_roll_deg | FLOAT | 0.00-1000000–1000000 | — |
| cam_distance | FLOAT | 0.0000-1000000–1000000 | — |
| interactive_state | STRING | {"pivot_x":0,"pivot_y":0,"pivot_z":0,"yaw_deg":0,"pitch_deg":0,"roll_deg":0,"distance":0} | — |
| output_width | INT | 102464–4096 | Raster width; viewer uses same aspect. |
| output_height | INT | 102464–4096 | Raster height; viewer uses same aspect. |
| use_source_resolution | BOOLEAN | false | If True and SHARP intrinsics are connected, render at the source image size (ignores width/height). |
| show_viewer_hud | BOOLEAN | true | Show the interactive viewer info overlay (camera, pivot, resolution). |
| enable_viewer | BOOLEAN | true | Mount the interactive viewer iframe. Disable to fully unload the viewer UI and WebGL context for this node. |
| seed | INT | 00–18446744073709550000 | Locked-shot variation seed. |
| rand_tx_min | FLOAT | 0.0000-1000000–1000000 | Locked shot: world-space X translation (scene units) added last, after orbit placement along composed view axis. |
| rand_tx_max | FLOAT | 0.0000-1000000–1000000 | — |
| rand_ty_min | FLOAT | 0.0000-1000000–1000000 | — |
| rand_ty_max | FLOAT | 0.0000-1000000–1000000 | — |
| rand_tz_min | FLOAT | 0.0000-1000000–1000000 | — |
| rand_tz_max | FLOAT | 0.0000-1000000–1000000 | — |
| rand_yaw_min | FLOAT | 0.00-1000000–1000000 | Locked shot: world-stage yaw (deg) as part of Rδ (Rz·Ry·Rx) left-applied after panel rotation. |
| rand_yaw_max | FLOAT | 0.00-1000000–1000000 | — |
| rand_pitch_min | FLOAT | 0.00-1000000–1000000 | — |
| rand_pitch_max | FLOAT | 0.00-1000000–1000000 | — |
| rand_roll_min | FLOAT | 0.00-1000000–1000000 | Final downstream roll jitter (deg). |
| rand_roll_max | FLOAT | 0.00-1000000–1000000 | — |
| rand_loc_pitch_min | FLOAT | 0.00-1000000–1000000 | Locked shot: camera-local pitch jitter (deg), right-applied after world Rδ (same Rz·Ry·Rx order in camera frame). |
| rand_loc_pitch_max | FLOAT | 0.00-1000000–1000000 | — |
| rand_loc_yaw_min | FLOAT | 0.00-1000000–1000000 | — |
| rand_loc_yaw_max | FLOAT | 0.00-1000000–1000000 | — |
| gaussian_scale | FLOAT | 1.00.1–64 | — |
| max_gaussians | INT | 00–200000 | 0 = no cap (all in-frustum splats). >0 keeps top-N by importance. |
| background | COMBO | black | 3 options: black, mid-gray, white |
| ply_pathopt | STRING | Path to a Gaussian Splatting PLY file | |
| extrinsicsopt | EXTRINSICS | Camera extrinsics from SHARP | |
| intrinsicsopt | INTRINSICS | Camera intrinsics from SHARP |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |