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

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 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, 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 (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. 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:

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:

  • 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 (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) 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 (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
  • 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 (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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →