Videos With OpenSpace

Using OpenSpace to Make Movies
Dr. James Hedberg
City College of New York


Dept. of Physics
Director of CCNY Planetarium

No more captive audiences

What are the rules of outreach & sci-comm?


Most are probably better described as guidelines

  1. Accessible

  2. Exciting

  3. Relevant

  4. Inspiring

  5. ...

Tell the Truth*

Flying ν

My Spark

Yet, the only way for such a geometry to exist, in which the planet's path through the sky would cross like a V or X, would be if their orbits were heavily tilted out of the ecliptic plane. Which according to the current models of the solar system is not correct.

How to make a v or x shape

One of my earliest animations showing the great conjunction of 2020.

Truth telling tools

Getting Started with Video Output

  1. Simple Screen Capture
  2. Output to OBS
  3. Session Recording
  4. API control

Method One: Session Recording

Use the normal navigation controls to control the camera. Record your motions, then replay with Output Frames.

This method is great for general, cinematic style views.

Method One: Session Recording

The limitations are primarily your skill as a pilot, and an inability to change properties while flying since we only have so many fingers and hands.

Method One: Session Recording

There are also little issues that can be annoying. Eg: tile Loading times. Even when referencing local data, there can be noticeable jumps in terrain as higher resolution tiles are loaded in.

Tips

1. Customize your window sizes for the media you want to make: Use these in openspace.cfg

        
-- VERTICAL VIDEO
SGCTConfig = sgct.config.single{675,1200,res={1080, 1920},vsync=false}

-- SQUARE
SGCTConfig = sgct.config.single{1080,1080,res={1080, 1080},vsync=false}
        
      

(Or make custom sgct configurations)

Tips

2. Use defined folders to save your images.

        
openspace.setScreenshotFolder(openspace.absPath("${USER}/screenshots/description"));
        
      

Record the Session

Render Frames

The images all in a folder

Now, all the images are in one folder, and are numbered sequentially.

Import the sequence into Premiere Pro (or whatever you want to use as a video editor.)

Select an Image Sequence on import

Now the images are one sequence

Now you can edit your video.

Fancy Pants

Layers

Now that the session is recorded, you can turn things on/off during various render passes and make layers to play with.

  1. Label Layer
  2. Trails Layer
  3. Date Time Layer

Layers

Layers

Layers

Customize Date Dashboard

Render just the date, and now you have a layer.

Method Two: Programmatic Control

"Every day at noon, the sun..."

"If you look to the southern skies about 40 minutes after sunset this week, you'll see..."

"Planets in retrograde motion move backwards with respect to the stars..."

seconds > J2000 DateTime
snapshotdate array
789022870.0 01 January 2025 17:00
789109270.0 02 January 2025 17:00
789195670.0 03 January 2025 17:00
789282070.0 04 January 2025 17:00
789368470.0 05 January 2025 17:00
789454870.0 06 January 2025 17:00
789541270.0 07 January 2025 17:00

Code excerpt

  
  for i, value in enumerate(snapshotdate):
    timestamp = str(value)
    
    message = json.dumps({"topic": 4,
                  "type": "luascript",
                  "payload": {"function": "openspace.time.setTime",
                              "arguments": [timestamp],
                              "return": False}})
    ws.send(message)
        
    print (".", end="", flush=True)
    
    time.sleep(0.1) 

    message = json.dumps({"topic": 4,
                  "type": "luascript",
                  "payload": {"function": "openspace.takeScreenshot",
                              "arguments": [],
                              "return": False}})
    ws.send(message)
    
    time.sleep(0.1)
  

Where is the Sun at Noon?

Get Sunset times

  
  import ephem
  import datetime

  gc = ephem.Observer()
  gc.lat, gc.lon = '35.9739', '-113.7689'
  gc.pressure = 0
  d = ephem.Date('2025/1/9')
  numberOfDays = 14
  sunrises = []
  sunsets = []

  for i in range(0,numberOfDays):
    gc.date = d+i
    tograbSunrise = ephem.to_timezone(gc.next_rising(ephem.Sun()), ephem.UTC)+datetime.timedelta(minutes=10)
    sunrises.append(tograbSunrise)
    tograbSunset = ephem.to_timezone(gc.next_setting(ephem.Sun()), ephem.UTC)+datetime.timedelta(minutes=1)
    sunsets.append(tograbSunset)
  
  

Prepare String for OpenSpace

    
    alltimes = []

    for i in range(0,numberOfDays):
      tograb = sunsets[i]
      timeandday = tograb.isoformat().replace('+00:00','')
      alltimes.append(timeandday)
    
  

Take the pictures

    
  number_of_photos = len(alltimes)
  for i in range(0, number_of_photos):
    timestamp = alltimes[i]
    message = json.dumps({"topic": 4,
                  "type": "luascript",
                  "payload": {"function": "openspace.time.setTime",
                              "arguments": [timestamp],
                              "return": False}})
    ws.send(message)
    time.sleep(0.5) 
    message = json.dumps({"topic": 4,
                  "type": "luascript",
                  "payload": {"function": "openspace.takeScreenshot",
                              "arguments": [],
                              "return": False}})
    ws.send(message)
    time.sleep(.5)
    
  

Simple GIF for 2 weeks of skywatching

Here's the result. It shows the positions of Venus and Saturn every night for 2 weeks at sunset time in the grand canyon.

Now, with layers, you can be the director of cinematography.

And, there are many other knobs to turn. (This one ramps the FOV from 90°-25° during the 2 weeks.)

Adjust FOV during a sequence.

    
  fovSetting = []
  for i, value in enumerate(alltimes): 
    fovSetting.append(90-70*i/len(alltimes))

  number_of_photos = len(alltimes)

  for i in range(0, number_of_photos):
    timestamp = alltimes[i]  
    message = json.dumps({"topic": 4,
        "type": "luascript",
        "payload": {"function": "openspace.time.setTime",
                "arguments": [timestamp],
                "return": False}})
    ws.send(message)

    message = json.dumps({"topic": 0,
        "type": "set",
        "payload": {"property": "RenderEngine.HorizFieldOfView",
                  "value": fovSetting[i],
                  "return": False}})
    ws.send(message)
  
    
  

Non-Linear Time

To highlight a particular event during a longer period of time, we can customize our $∆t$ values in the array.


totalimages = 600
intervals = []

a = 3550
b = totalimages/2
c = 110

for i in range(totalimages):
  intervalsCalc = -a*math.exp(-((i-b)**2)/(2*c**2) )+ 3600
  if intervalsCalc < 120 : 
  intervalsCalc = 120
  intervals.append(round(intervalsCalc,4))
    

Astronomical Time Scales

Experimental

Here we have the alt/azi of Mercury in a table. The camera is then rotated to always point at the position in the sky.


    message = json.dumps({"topic": 0,
      "type": "set",
      "payload": {"property": "Scene.CameraParent.Rotation.Rotation",
                  "value": [0,altitude,azimuth],
                  "return": False}})

    ws.send(message)
    

Here, we are tacking the position of Mars, advancing time, and adjusting the FOV.

Other formats/media

Full Dome


SGCTConfig = sgct.config.fisheye{1024, 1024}
  

VR


"projection": {
  "type": "EquirectangularProjection",
  "quality": "1k"
}
  

(Just make sure you have some extra drives.)

Ptolemaic Star Positions

General Tips

  • Customize things!

    • Fonts: Mono = "${FONTS}/Bitstream-Vera-Sans-Mono/VeraMono.ttf"
    • Dates: ::UTC-h:m (from timout_c)
    • Labels / Localizations

Short Vertical Videos

@ccnyplanetarium

Tell the Truth*

Thank you