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, and optionMenu
  • File dialog for directory browsing - using fileDialog2 with fileMode=3 for 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)
← Prev Next ->