Headless Renders with OpenUSD

OpenUSD offers a headless rendering script. Let's play around with it and see if we can remove a Qt dependency!

Headless Renders with OpenUSD
Babaef, young and old. Metropolitan Museum of Art, New York. Fletcher Fund 1964

Getting headless renders is actually pretty easy once you have a working USD install.

If you've got all the dependencies and you can render with usdview, you can run the usdrecord script to get a rendered image. It's located in the bin directory of your USD install if it was built with rendering support. I'll use the Kitchen_set example asset since it's easy to come by.

usdrecord Kitchen_set/Kitchen_set.usd kitchen.png

You should get an output image like this:

kitchen.png

Nice and easy! usdrecord has command line parameters to choose which frames to render, which camera to use, and lots of other things. Run usdrecord -h to get a list.

That was too easy. Let's try something harder 😈

Removing the Qt Dependency

Recently I wanted to get headless renders in a docker container, and I wanted to keep the docker image size small. I found that the Qt libraries were contributing hundreds of megabytes to the image size. Since I wanted headless renders I thought I could get by without them.

When I removed Qt from the image I found that usdrecord would fail with an error like

Traceback (most recent call last):
  File "/opt/USD/bin/usdrecord", line 45, in _SetupOpenGLContext
    from PySide6.QtOpenGLWidgets import QOpenGLWidget
ModuleNotFoundError: No module named 'PySide6'

It turns out usdrecord uses Qt to create a window handle and manage the OpenGL context it needs for rendering. If you don't need to do an OpenGL render you don't need to do this, but I wanted the kind of OpenGL preview renders you get from usdview, so I wanted to make OpenGL work.

The glfw library can set up window handles and give you an OpenGL context. It's much lighter weight than Qt and has python bindings. The error above references a function called _SetupOpenGLContext. I removed all the Qt related imports from usdrecord and replaced that function with the version below. Then, after I ran pip install glfw, I could run usdrecord without Qt.

def _SetupOpenGLContext(width=100, height=100):
    import glfw

    glfw.init()
    glfw.window_hint(glfw.VISIBLE, glfw.FALSE)
    window = glfw.create_window(width, height, "offscreen buffer", None, None)
    if not window:
        glfw.terminate()
        return None
    glfw.make_context_current(window)
    return window

Once I knew that worked I wrote up a new render launching script that doesn't use Qt. You can see the script I wound up using on github here.

My Docker Build

If you'd like to use that docker image to get usdrecord like renders, feel free. The image is called rstelzleni/usd-alpine on dockerhub. The gl-24.05 version has OpenGL support and is about a 140MB download. If your Kitchen_set (or other USD content) is in the current directory you can run it like below and get the output png file back in the current directory.

docker run --rm -v.:/data rstelzleni/usd-alpine:gl-24.05 /data/Kitchen_set/Kitchen_set.usd /data/kitchen.png