Minimal Working Frame Recorder

Hello,

I am trying to set up Frame Recorder, but since Python docs don’t build properly on 3.9.13 (for 23.08), I am referencing the C++ docs and trying to work from there. So far, I have defined a RenderSettingsBase on “/Render” and have set the resolution, and added a CameraRel to my camera (at “/Camera”). Now I want to set up a frame recorder to actually generate the images, however I can’t seem to figure out what arguments it takes.

Looking at “usdrecord.py” I tried:

renderer = UsdAppUtils.rendererArgs.GetPluginIdFromArgument("GL")
rs_prim_path = stage.SetMetadata("renderSettingsPrimPath, "/RenderSettings/Prim") 

frame_recorder = UsdAppUtils.FrameRecorder(renderer, False, rs_prim_path)

But it throws an error that it did not match the C++ signature. Would someone be able to clarify where I’m going wrong? In my reading, I read something about needed to set up an “OpenGL Context” but I’m not sure what that is/how to do it, and I also am not sure if I am authoring everything I need to be properly.

The full code:

# Get the stage and camera
stage = Usd.Stage.Open("example.usda")
camera = UsdGeom.Camera(stage.GetPrimAtPath("/Camera"))

# Create a renderer prim
render_prim = UsdGeom.Xform.Define(stage, "/RenderSettings/Prim")

horiz_ap = int(camera.GetHorizontalApertureAttr().Get())
vert_ap = int(camera.GetVerticalApertureAttr().Get())

# Camera resolution is the product of the apertures
cam_res = horiz_ap * vert_ap

# Camera aspect ratio is horizontal : vertical or the apertures
cam_asp = horiz_ap / vert_ap

# Define the renderer settings object
render_base = UsdRender.SettingsBase(render_prim)

# Add rendering parameters
render_base.CreateResolutionAttr().Set(Gf.Vec2i([horiz_ap, vert_ap]))
render_base.CreateCameraRel().AddTarget("/Camera")


renderer = UsdAppUtils.rendererArgs.GetPluginIdFromArgument("GL")
rs_prim_path = stage.SetMetadata("renderSettingsPrimPath", "/RenderSettings/Prim")

frame_recorder = UsdAppUtils.FrameRecorder(renderer, False, rs_prim_path)

Hi @Dinonel , I recategorized this as a Hydra post, because both the GL Context question, and the issues you’ll run into after that, related to the information in your RenderSettings setup getting transmitted to and consumed by your renderer of choice, will be best answered by the folks over there!

Hi all,

After MUCH searching and scouring, I was able to create a minimal working Frame Recorder, so I will leave the code here so that others don’t have to look through as I did.

from pxr import UsdRender, Usd, UsdGeom, Gf, UsdAppUtils
import os
import sys


def SetupOpenGLContext(width=100, height=100):
    from PySide6.QtOpenGLWidgets import QOpenGLWidget
    from PySide6.QtOpenGL import QOpenGLFramebufferObject
    from PySide6.QtOpenGL import QOpenGLFramebufferObjectFormat
    from PySide6.QtCore import QSize
    from PySide6.QtGui import QOffscreenSurface
    from PySide6.QtGui import QOpenGLContext
    from PySide6.QtGui import QSurfaceFormat
    from PySide6.QtWidgets import QApplication

    application = QApplication(sys.argv)

    glFormat = QSurfaceFormat()
    glFormat.setSamples(4)

    glWidget = QOffscreenSurface()
    glWidget.setFormat(glFormat)
    glWidget.create()

    glWidget._offscreenContext = QOpenGLContext()
    glWidget._offscreenContext.setFormat(glFormat)
    glWidget._offscreenContext.create()

    glWidget._offscreenContext.makeCurrent(glWidget)

    glFBOFormat = QOpenGLFramebufferObjectFormat()
    glWidget._fbo = QOpenGLFramebufferObject(QSize(1, 1), glFBOFormat)
    glWidget._fbo.bind()

    return glWidget


# Get the stage and camera
stage = Usd.Stage.Open("RotateCamera.usda")
camera = UsdGeom.Camera(stage.GetPrimAtPath("/Camera"))

# anim_length = stage.GetEndTimeCode()
anim_length = 100

# Create a renderer prim
render_prim = UsdGeom.Xform.Define(stage, "/Render")

horiz_ap = int(camera.GetHorizontalApertureAttr().Get())
vert_ap = int(camera.GetVerticalApertureAttr().Get())

# Camera resolution is the product of the apertures
cam_res = horiz_ap * vert_ap

# Camera aspect ratio is horizontal : vertical or the apertures
cam_asp = horiz_ap / vert_ap

# Define the renderer settings object
render_base = UsdRender.Settings(render_prim)

# Add a bunch of rendering parameters
render_base.CreateResolutionAttr().Set(Gf.Vec2i([horiz_ap, vert_ap]))
render_base.CreateCameraRel().AddTarget("/Camera")

GPU = True

if GPU:
    glWidget = SetupOpenGLContext(100, 100)

renderer = UsdAppUtils.rendererArgs.GetPluginIdFromArgument("GL")
rs_prim_path = stage.SetMetadata("renderSettingsPrimPath",
                                 "/Render")

frame_recorder = UsdAppUtils.FrameRecorder(renderer, GPU)
frame_recorder.SetComplexity(1)  # 1 is the lowest

frame_recorder.SetRendererPlugin(renderer)
for frame in range(int(anim_length)):
    frame_recorder.Record(stage, camera, frame, f".\\Renders\\test{frame}.png")

Things to note:

  • This was made using USD 23.08 and PySide6. Looking at the documentation for 23.11, FrameRecorder takes a new argument which is what I originally had which is rsPrimPath. For those of you using 23.11, this will ensure that your frame recorder uses your designated Render Settigns. I’m still trying to figure out how to connect my render settings to the frame recorder in 23.08 and will update here if I ever do figure it out.
  • Most of this code is an amalgamation of usdrecord.py and various other snippets of code found online, as well as stuff developed through trial and error.
  • There is a surprising lack of any form of “beginner” documentation with regard to rendering so if you do find any please share it, and I will also try to generate something myself for others in the future.
4 Likes

Update:

Spiff clarified for me that as of right now (USD 23.11) only the prman renderer supports render settings, so I would assume that means to set things such as a custom pixel resolution one would need to make their own renderer using the imagingGLEngine schema.