Smooth Camera with Py-SPHViewer

Creating a Smooth Camera Trajectory with Py-SPHViewer

Py-SPHViewer allows users to define custom smooth camera trajectories. The tool responsible for this is called camera_tools, initially designed by Adrien Thob and inspired by Adobe Surge Target.

This tutorial will guide you through creating a custom camera trajectory using this powerful tool.

Setting Up the Scene

In this example, we will create a universe with two cubes, located at \((x_1, y_1, z_1) = (0.5, 1.5, 0.5)\) and \((x_2, y_2, z_2) = (0.5, -5.5, 0.5)\) respectively:

import matplotlib.pyplot as plt
import numpy as np
import h5py
from sphviewer.tools import camera_tools

n1 = 10000

cube1 = np.random.rand(3, n1)
cube1[:, 1] -= 6
cube2 = np.random.rand(3, n1)
cube2[:, 1] += 1
cubes = np.concatenate((cube1, cube2), axis=0)
mass = np.ones(n1 + n1)

Initializing Particles, Scene, and Camera Objects

Instead of using QuickView, we will render the scene more efficiently by directly using Py-SPHViewer’s internal functions.

Here, we instantiate the Particles and Scene classes. The camera is automatically created when the scene is initialized:

P = sph.Particles(cubes, mass)
S = sph.Scene(P)

You can update the camera parameters using the Scene.update_camera() method.

Defining the Camera Trajectory

Before creating the camera trajectory, it’s helpful to decide what you want to visualize and when you want to see it. For example, let’s say we want a video of 1680 frames, with the following sequence:

  1. The camera starts focused on the first cube at frame 0, then begins moving toward the second cube by frame 180.
  2. It remains focused on the second cube until frame 750, then starts moving back to the first cube.
  3. The camera reaches the first cube at frame 930 and stays there until frame 1500, after which it moves toward the second cube, arriving at frame 1680.

This sequence defines the camera’s trajectory using “anchors”. camera_tools smoothly interpolates the trajectory between these anchor points. Here’s how you define it:

cm_1 = [0.5, 1.5, 0.5]
cm_2 = [0.5, -5.5, 0.5]

targets = [cm_1, cm_2]

anchors = {}
anchors['sim_times'] = [0.0, 1.0, 'pass', 3.0, 'same', 'same', 'same']
anchors['id_frames'] = [0, 180, 750, 840, 930, 1500, 1680]
anchors['r']         = [2, 'same', 'same', 'same', 'same', 'same', 'same']
anchors['id_targets'] = [0, 1, 'same', 'pass', 0, 'same', 1]
anchors['t']         = [0, 'same', 'same', 'same', 'same', 'same', 0]
anchors['p']         = [0, 'same', 'same', 'same', 'same', 'same', 'same']
anchors['zoom']      = [1., 'same', 'same', 'same', 'same', 'same', 'same']
anchors['extent']    = [10, 'same', 'same', 'same', 'same', 'same', 30]

data = camera_tools.get_camera_trajectory(targets, anchors)

h = 0
for i in data:
    i['xsize'] = 250
    i['ysize'] = 250
    i['roll'] = 0
    S.update_camera(**i)
    R = sph.Render(S)
    img = R.get_image()
    R.set_logscale()
    plt.imsave('img/image_' + str('%04d.png' % h), img, vmin=0, vmax=6, cmap='cubehelix')
    h += 1

Here, the anchors dictionary defines the key camera parameters, such as zoom, extent, r, t, p, and the indices of targets. These anchors are passed to the camera_tools.get_camera_trajectory() function, which generates the camera’s parameters for each frame.

Special keywords like same and pass control the behavior of the camera at each anchor. same means the current parameter will remain unchanged, while pass interpolates the value between the previous and next anchors.

The result of the code above is:

Smooth Camera Tutorial

Adding More Motion

To make the camera movement more dynamic, we can also rotate the camera, adjust its distance from the cubes, and more. Here’s an example with additional parameters:

anchors = {}
anchors['sim_times'] = [0.0, 1.0, 'pass', 3.0, 'same', 'same', 'same']
anchors['id_frames'] = np.array([0, 180, 750, 840, 930, 1500, 1680]) / 4
anchors['id_targets'] = [0, 1, 'same', 'pass', 0, 'same', 1]
anchors['extent']    = [10, 'same', 'same', 'same', 'same', 'same', 30]

anchors['r']         = [10, 2, 'same', 4, 2, 'same', 10]
anchors['t']         = [0, 'pass', 'pass', 45, 'pass', 'pass', 0]
anchors['p']         = [0, 'pass', 'pass', 'pass', 'pass', 'pass', 900]
anchors['extent']    = [10, 'pass', 'pass', 'pass', 'pass', 'pass', 30]

This results in the following animation:

Advanced Smooth Camera

These examples demonstrate the flexibility and power of camera_tools. You can combine various camera parameters to create impressive visualizations. For instance, the video below was generated using camera_tools: