# How to Customize the Camera Path for the Offline Rendering Pipeline in LingBot-Map

> Customize your LingBot-Map camera path easily for offline rendering. Control trajectories via UI or code before GLB export for stunning visual results.

- Repository: [Robbyant/lingbot-map](https://github.com/Robbyant/lingbot-map)
- Tags: how-to-guide
- Published: 2026-07-21

---

**The offline rendering pipeline in LingBot-Map constructs camera trajectories from reconstruction poses stored in `self.all_steps`, enabling full customization through UI controls in [`point_cloud_viewer.py`](https://github.com/Robbyant/lingbot-map/blob/main/point_cloud_viewer.py) or direct programmatic manipulation before GLB export.**

The LingBot-Map repository (`Robbyant/lingbot-map`) provides a comprehensive visualization toolkit for 3D reconstructions. Customizing the camera path for the offline rendering pipeline allows precise control over which poses are rendered, their spatial density, and the trajectory's visual representation.

## Understanding the Camera Trajectory Architecture

The offline rendering pipeline centers on the **camera trajectory** stored in the `PointCloudViewer` class. This trajectory is maintained as a Python list called `self.all_steps` in [`lingbot_map/vis/point_cloud_viewer.py`](https://github.com/Robbyant/lingbot-map/blob/main/lingbot_map/vis/point_cloud_viewer.py), which contains the ordered sequence of camera extrinsics (`cam2world_3x4` matrices) derived from the reconstruction.

When you initiate an export, the viewer iterates over `self.all_steps` and passes each pose to `integrate_camera_into_scene` in [`lingbot_map/vis/glb_export.py`](https://github.com/Robbyant/lingbot-map/blob/main/lingbot_map/vis/glb_export.py) (approximately line 770). This function constructs the visual representation—including camera frustums and the connecting trajectory tube—based on the current state of the camera list.

## Method 1: Control Camera Visibility via UI Controls

The simplest way to customize the camera path is through the interactive controls defined in [`point_cloud_viewer.py`](https://github.com/Robbyant/lingbot-map/blob/main/point_cloud_viewer.py). Two key UI elements govern which frames appear in the final render:

- **`self.show_camera_checkbox`** (defined around line 414): Toggles the visibility of camera frustums in the scene.
- **`self.camera_downsample_slider`** (defined around line 421): Controls the sampling rate of the trajectory, allowing you to render every N-th frame instead of the full sequence.

When you adjust these controls, the viewer calls `_regenerate_cameras()` (approximately line 606) to rebuild the visualization according to the new parameters. This method filters `self.all_steps` based on the downsample value and updates the scene geometry.

## Method 2: Programmatically Modify the Camera Path

For precise control over the camera path, directly manipulate the `self.all_steps` list before triggering the export. This approach allows you to reverse the trajectory, insert interpolated poses, or prune specific frames.

The following workflow demonstrates how to load a reconstruction, customize the trajectory, and export the result:

```python
from lingbot_map.vis.point_cloud_viewer import PointCloudViewer

# Initialize the viewer with reconstruction data

viewer = PointCloudViewer(
    point_clouds=my_point_clouds,
    camera_poses=my_cam_poses,
    show_camera=True,
    camera_downsample=2,
)

# Customize the trajectory: keep every 5th frame

custom_traj = viewer.all_steps[::5]
viewer.all_steps = custom_traj

# Export the customized scene

viewer.export_glb("custom_scene.glb")

```

By modifying `viewer.all_steps` directly, you override the default sequence and define exactly which poses the offline renderer processes.

## Adjusting Visual Parameters for Camera Frustums

Beyond controlling which cameras appear, you can customize their visual appearance through additional sliders in [`point_cloud_viewer.py`](https://github.com/Robbyant/lingbot-map/blob/main/point_cloud_viewer.py):

- **Scale**: Controlled by the camera size slider (`self.camera_downsample_slider`), which adjusts the frustum dimensions passed to the export function.
- **Thickness**: Controlled by `self.camera_thickness_slider` (defined around line 462), which determines the edge weight of the trajectory tube.

These parameters are passed to `integrate_camera_into_scene` during the export process, determining the size of the camera frustums and the thickness of the trajectory tube mesh. The `CameraState` helper type in [`lingbot_map/vis/utils.py`](https://github.com/Robbyant/lingbot-map/blob/main/lingbot_map/vis/utils.py) (line ~22) supports these rendering operations.

## The Export Pipeline and GLB Generation

When you call `export_glb()`, the viewer executes the following sequence:

1. Iterates over the current `self.all_steps` list
2. Extracts the `cam2world_3x4` extrinsic matrix for each entry
3. Invokes `integrate_camera_into_scene` (from [`lingbot_map/vis/glb_export.py`](https://github.com/Robbyant/lingbot-map/blob/main/lingbot_map/vis/glb_export.py)) at approximately line 739
4. Generates a GLB file containing the camera frustums and trajectory tube

For headless rendering workflows, refer to [`benchmark/viewer.py`](https://github.com/Robbyant/lingbot-map/blob/main/benchmark/viewer.py) (lines ~1266-1270), which demonstrates how to request renders from a `viser` client without the full GUI.

## Summary

- The camera trajectory is stored in `self.all_steps` within [`lingbot_map/vis/point_cloud_viewer.py`](https://github.com/Robbyant/lingbot-map/blob/main/lingbot_map/vis/point_cloud_viewer.py)
- Use `self.show_camera_checkbox` and `self.camera_downsample_slider` (lines ~414 and ~421) to control visibility and density via the UI
- Call `_regenerate_cameras()` (line ~606) to apply UI changes to the visualization
- Modify `self.all_steps` directly to reverse, prune, or reorder the camera path programmatically
- Adjust `self.camera_thickness_slider` (line ~462) to change the visual weight of trajectory lines
- The `integrate_camera_into_scene` function in [`glb_export.py`](https://github.com/Robbyant/lingbot-map/blob/main/glb_export.py) (line ~770) constructs the final geometry for export

## Frequently Asked Questions

### Where is the camera trajectory data stored in LingBot-Map?

The camera trajectory is stored in the `self.all_steps` list inside the `PointCloudViewer` class in [`lingbot_map/vis/point_cloud_viewer.py`](https://github.com/Robbyant/lingbot-map/blob/main/lingbot_map/vis/point_cloud_viewer.py). This list contains the ordered camera extrinsics (`cam2world_3x4` matrices) that define the path through the reconstruction.

### How do I reduce the number of camera frames in the rendered output?

Adjust the `self.camera_downsample_slider` value (defined around line 421) to sample every N-th frame, or programmatically slice the `self.all_steps` list (e.g., `viewer.all_steps[::5]`) to keep only specific poses before calling `export_glb()`.

### Can I export a custom camera path without using the GUI?

Yes. Instantiate `PointCloudViewer` programmatically, modify the `self.all_steps` list directly to define your custom trajectory, and invoke `viewer.export_glb("filename.glb")`. This headless approach requires no interactive UI components.

### What parameters control the visual size of camera frustums?

The visual scale is controlled by the camera size slider (`self.camera_downsample_slider`), while the edge thickness is governed by `self.camera_thickness_slider` (line ~462). Both values are passed to `integrate_camera_into_scene` during GLB construction to determine the final mesh geometry.