Python Tools - Session 9B
Render Camera Rig System
March 5, 2020
Session Navigation
Overview
This session builds a complete circular camera rig system. The tool positions multiple cameras around a selected object on a NURBS circle, then renders from each camera to disk using Arnold. A PyMEL UI provides sliders for camera count and radius, a file browser for output directory, and a resolution dropdown.
Key Concepts
- Creating circular camera rigs in Maya - positioning cameras evenly around a subject
- Camera positioning around circular paths using NURBS curves - using
getPointAtParam()to place cameras along a circle - Render output management and file system operations - creating directories and managing output file paths
- Arnold renderer integration - rendering images with
pm.arnoldRender()and configuring the Arnold driver - UI sliders and dropdowns for numerical control -
intSliderGrp,floatSliderGrp, andoptionMenu - File dialog for directory browsing - using
fileDialog2withfileMode=3for directory selection
Code
The complete render camera rig tool with UI (camera_rig.py):
import pymel.core as pm
import os
CAM_NAME = 'render_cam'
class RenderRig():
def __init__(self):
self.handle = "CameraRenderRig"
if pm.window(self.handle, exists=True):
pm.deleteUI(self.handle)
if pm.windowPref(self.handle, exists=True):
pm.windowPref(self.handle, remove=True)
with pm.window(self.handle, title="Create Render Rig", width=350, height=100) :
with pm.columnLayout():
self.camera_count = pm.intSliderGrp(min=1, max=20, value=5, field=True, label="Camera Count")
self.radius = pm.floatSliderGrp(min=1, max=20, value=10, field=True, label="Rig Radius")
pm.button(label="Create Rig From Selected Objects", height=30, command=pm.Callback(self.build_rig))
with pm.rowLayout(numberOfColumns=2):
pm.button(label="Browse", width=80, height=30, command=pm.Callback(self.browse))
self.target_dir = pm.textField(width=250)
with pm.rowLayout(numberOfColumns=3):
self.resolution = pm.optionMenu(label="Resolution")
pm.menuItem(label='512')
pm.menuItem(label='1024')
pm.menuItem(label='2048')
pm.menuItem(label='4096')
pm.button(label="Render Rig", width=100, height=30, command=pm.Callback(self.render_rig))
pm.showWindow()
def browse(self):
files = pm.fileDialog2(dialogStyle=1, fileMode =3)
if files is None:
return
self.target_dir.setText(files[0])
def make_camera(self, aim_pos, cam_pos):
cam = pm.camera(name=CAM_NAME)
pm.mel.objectMoveCommand()
pm.mel.cameraMakeNode(2, '')
aim_node = cam[0] + '_aim'
pm.move(aim_node, aim_pos)
pm.move(cam[0], cam_pos)
def build_rig(self):
selected_nodes = pm.ls(selection=True)
bb = pm.exactWorldBoundingBox()
center = [((bb[3]+bb[0])/2.0), (bb[4]+bb[1])/2, (bb[5]+bb[2])/2]
pm.spaceLocator(position=center)
circ = pm.circle(radius=self.radius.getValue(), nrx=0, nry=1, nrz=0)[0]
pm.move(circ, center)
length = circ.length()
param = circ.findParamFromLength(length)
num_cams = self.camera_count.getValue()
curve_pos = 0.0
for obj in range(0, num_cams):
curve_pos = curve_pos + (param/num_cams)
world_pos = circ.getPointAtParam(curve_pos, space='world')
self.make_camera(center, world_pos)
def render_rig(self):
res = int(self.resolution.getValue())
target_directory = self.target_dir.getText()
if not os.path.exists(target_directory):
os.makedirs(target_directory)
for cam in pm.ls(type='camera'):
if CAM_NAME in cam.name():
render_path = os.path.join(target_directory, '%s.jpg' %cam.name())
render(render_path, cam, w=res, h=res)
def render(render_path, camera, w=512, h=512, extension='jpeg'):
print 'Rendering to %s' %render_path
pm.setAttr('defaultArnoldDriver.ai_translator', extension, type='string')
pm.setAttr('defaultArnoldDriver.pre', render_path, type='string')
pm.arnoldRender(w=w, h=h, cam=camera.name(), origFileName=render_path)