Getting your cameras right

Hey,

when we’re animating cameras it’s most of the time needed to change the timing of the camera-move without changing it’s movement. So our approach is to link curves to others. In Softimage this is called link-with, in Maya set-driven-key and in Houdini we can use the function chf. In this tutorial I’ll explain the concept of animating cameras with this principle and how to setup the cameras in the different DCC.

Lets start. If you imagine a camera has an start- and an endpoint. The start point is always Position 0 and the endpoint is always 1. The hole movement of both happens between this two points. Now imagine an fcurve with values from 0-1 that controls the exact point of camera & interest in time. That’s how it works. It doesn’t matter if you time your camera on 50 frames or on 500, the camera animation keeps the same.

1. Softimage

This would be the quick and dirty approach in Softimage. Crating a Model, Camera_Root, Camera and Camera_Interest. Further more we need a custom property with three attributes Camera_Animation, Focal_Length and Camera_Roll. In Softimage this looks like this:

#Set Application as xsi
xsi = Application

#Deselect all Objects
xsi.DeselectAll()
#Create a Model
xsi.CreateModel("", "Camera_Rig", "", "")
#Create a Camera with the Model as Parent
xsi.GetPrimCamera("", "", "Camera_Rig", "", "", "")
#Rest the Transforms of the Model Root
xsi.ResetTransform("Camera_Rig.Camera_Root", "siObj", "siSRT", "siXYZ")
#Brig the Camera and Interest into Place
xsi.SetValue("Camera_Rig.Camera.kine.local.posz", 10, "")
xsi.ResetTransform("Camera_Rig.Camera_Interest", "siObj", "siSRT", "siXYZ")
#Setup an HD camera with an Alexa Backplate
xsi.SetValue("Camera_Rig.Camera.camera.projplane", True, "")
xsi.SetValue("Camera_Rig.Camera.camera.std", 18, "")
xsi.SetValue("Camera_Rig.Camera.camera.projplanewidth", 0.935, "")
#Add the Animation Property to the Model
xsi.SelectObj("Camera_Rig", "", "")
xsi.AddProp("Custom_parameter_list", "", "", "Animation", "")
#Create an Parameter for Animation, FocalLenth, and Roll
xsi.SIAddCustomParameter("Camera_Rig.Animation", "Camera_Animation", "siDouble", 0, -10, 10, "", 2053, "", 1, "", "")
xsi.SIAddCustomParameter("Camera_Rig.Animation", "Camera_FocalLength", "siDouble", 50, 1, 300, "", 2053, 1, 300, "", "")
xsi.SIAddCustomParameter("Camera_Rig.Animation", "Camera_Roll", "siDouble", 0, -360, 360, "", 2053, -360, 360, "", "")
#Keyframe the Camera form 1-100
xsi.SetValue("PlayControl.Current", 1, "")
xsi.SetValue("Camera_Rig.Animation.Camera_Animation", 0, "")
xsi.SaveKey("Camera_Rig.Animation.Camera_Animation", 1, "", "", "", "", "")
xsi.SetValue("PlayControl.Current", 100, "")
xsi.SetValue("Camera_Rig.Animation.Camera_Animation", 1, "")
xsi.SaveKey("Camera_Rig.Animation.Camera_Animation", 100, "", "", "", "", "")
xsi.SetValue("PlayControl.Current", 1, "")
#Link the Roll and FocalLength to the Custom PPG
xsi.CopyPaste("Camera_Rig.Animation.Camera_Roll", "", "Camera_Rig.Camera.kine.dircns.roll", 1)
xsi.CopyPaste("Camera_Rig.Animation.Camera_FocalLength", "", "Camera_Rig.Camera.camera.projplanedist", 1)
#Link the Tansforms of Camera and Root to the Animation Parameter
xsi.SetExpr("Camera_Rig.Camera.kine.local.posx", "l_fcv( Camera_Rig.Animation.Camera_Animation )", "")
xsi.SetExpr("Camera_Rig.Camera.kine.local.posy", "l_fcv( Camera_Rig.Animation.Camera_Animation )", "")
xsi.SetExpr("Camera_Rig.Camera.kine.local.posz", "l_fcv( Camera_Rig.Animation.Camera_Animation )", "")
xsi.SetExpr("Camera_Rig.Camera_Interest.kine.local.posz", "l_fcv( Camera_Rig.Animation.Camera_Animation )", "")
xsi.SetExpr("Camera_Rig.Camera_Interest.kine.local.posy", "l_fcv( Camera_Rig.Animation.Camera_Animation )", "")
xsi.SetExpr("Camera_Rig.Camera_Interest.kine.local.posx", "l_fcv( Camera_Rig.Animation.Camera_Animation )", "")
#Set the related Values for Camerapostion 0 and Camerapostion 1
xsi.SetValue("PlayControl.Current", 1, "")
xsi.SetRelativeValues("Camera_Rig.Camera.kine.local.posx,Camera_Rig.Camera.kine.local.posy,Camera_Rig.Camera.kine.local.posz,Camera_Rig.Camera_Interest.kine.local.posz,Camera_Rig.Camera_Interest.kine.local.posy,Camera_Rig.Camera_Interest.kine.local.posx")
xsi.SetValue("PlayControl.Current", 100, "")
xsi.SetRelativeValues("Camera_Rig.Camera.kine.local.posx,Camera_Rig.Camera.kine.local.posy,Camera_Rig.Camera.kine.local.posz,Camera_Rig.Camera_Interest.kine.local.posz,Camera_Rig.Camera_Interest.kine.local.posy,Camera_Rig.Camera_Interest.kine.local.posx")
xsi.SetValue("PlayControl.Current", 1, "")

Further more it is handy to map some small lines of code to a key. That makes position changes more easy.

#Import all maya commands
Application.LogMessage("SF_SetRelativeValuesonLocals_Execute called",constants.siVerbose)
for i in range(0,Application.Selection.Count):
	oObj = Application.Selection(i)
	oTempname = oObj.FullName
	Application.SetRelativeValues(str(oTempname) + ".kine.local.*")	

2. Maya

This would be the quick and dirty approach in Maya. As you notice, the general approach is the same. Creating a Top Null (In SI the Model) Camera_Root, Camera and Camera_Interest. The custom attributes were linked to the Camera_Rig Null Object.

import maya.cmds as cmd
import maya.mel

#Deselect all Objects
cmds.select( clear=True )
#Create an Root Locator
oCameraRig = cmds.spaceLocator( p=(0,0,0), n=("Camera_Rig"))[0]
#Create a Camera with the Root Locator as Parent
oCamera = maya.mel.eval("camera -centerOfInterest 1 -focalLength 50 -lensSqueezeRatio 1 -cameraScale 1 -horizontalFilmAperture 0.935 -horizontalFilmOffset 0 -verticalFilmAperture 0.5259 -verticalFilmOffset 0 -filmFit Horizontal -overscan 1 -motionBlur 0 -shutterAngle 144 -nearClipPlane 0.1 -farClipPlane 10000 -orthographic 0 -orthographicWidth 30 -panZoomEnabled 0 -horizontalPan 0 -verticalPan 0 -zoom 1;")
maya.mel.eval("objectMoveCommand;")
maya.mel.eval('cameraMakeNode 2 "";')
#Assign Variables for the different Objects
oCameraRoot = cmds.listRelatives(oCamera, ap=True)
oCameraInterest = cmds.listRelatives(oCameraRoot[0], c=True)
#Parent Camera Root
cmds.parent(  oCameraRoot[0], oCameraRig )
#Brig the Camera and Interest into Place
cmds.setAttr(str(oCamera[0]) + ".translateZ", 10)
cmds.setAttr(str(oCameraInterest[1]) + ".translateZ", 0)
#Create an Parameter for Animation, FocalLenth, and Roll
cmds.addAttr(oCameraRig, longName="Camera_Animation", at='double', min=-10, max=10, dv=0)
cmds.setAttr(oCameraRig+'.Camera_Animation', edit=1, k=1)
cmds.addAttr(oCameraRig, longName="Camera_FocalLength", at='double', min=-1, max=300, dv=50)
cmds.setAttr(oCameraRig+'.Camera_FocalLength', edit=1, k=1)
cmds.addAttr(oCameraRig, longName="Camera_Roll", at='double', min=-360, max=360, dv=0)
cmds.setAttr(oCameraRig+'.Camera_Roll', edit=1, k=1)
#Keyframe the Camera form 1-100
cmds.currentTime(1)
cmds.setKeyframe(oCameraRig+'.Camera_Animation')
cmds.currentTime(100)
cmds.setAttr(oCameraRig+'.Camera_Animation', 1)
cmds.setKeyframe(oCameraRig+'.Camera_Animation')
cmds.keyTangent( oCameraRig+'.Camera_Animation', itt="linear",ott="linear")
#Link the Tansforms of Camera and Root to the Animation Parameter
cmds.currentTime(1)
cmds.setDrivenKeyframe( oCamera[0]+'.translateX', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCamera[0]+'.translateY', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCamera[0]+'.translateZ', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCameraInterest[1]+'.translateX', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCameraInterest[1]+'.translateY', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCameraInterest[1]+'.translateZ', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCameraRig+'.Camera_Roll', cd=oCameraRig+'.Camera_Animation' )
cmds.currentTime(100)
cmds.setDrivenKeyframe( oCamera[0]+'.translateX', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCamera[0]+'.translateY', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCamera[0]+'.translateZ', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCameraInterest[1]+'.translateX', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCameraInterest[1]+'.translateY', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCameraInterest[1]+'.translateZ', cd=oCameraRig+'.Camera_Animation' )
cmds.setDrivenKeyframe( oCameraRig+'.Camera_Roll', cd=oCameraRig+'.Camera_Animation' )
cmds.currentTime(1)
cmds.connectAttr( oCameraRig+'.Camera_Roll', oCameraRoot[0]+'.twist')
#Reneame Objs
cmds.rename(oCameraRoot[0],"Camera_Root")
cmds.rename(oCamera[0],"Camera")
cmds.rename(oCameraInterest[1],"Camera_Interest")
#Deselect all Objects
cmds.select( clear=True )

As in Softimage it is also in Maya quite tricky to update the set driven keys. I highly recommend to map this lines of code to a key – So it is just a question of selection objects and updating the camera positions.

import maya.cmds as cmds

#Get current Selecton
selection= cmds.ls(selection=True )

#Loop through the Selecton 
for sel in selection:
    #Set Driven Key
    cmds.setDrivenKeyframe(str(sel))

3. Houdini

As we all know, Houdini is not the best tool for Keyframe animation, but it is somehow possible to setup something similar as in Softimage and Maya. It is just a different approach. I have made a small prototype of the setup I have explained. It is not finished, but it is a beginning that shows how it could work. In a real production environment I would recommend to setup the camera animation in one of the other packets.

Ok, first of all we need a Camera_Root (a Null), a Camera and a Camera_Interest. This looks like this.

On the Camera_Root we have three attributes a global animation (the timing curve), a camera animation and a camera_interest animation. All three channels of Camera_Postion and Camera_Interest were keyframed from 0-1 and the Camera_Animation from, lets say 1-100 with the vales 0-1. The magic happens in the Camera and Camera_Interest Nodes. There we use this function:

chf(“../Camera_Root/Interest_Positionx”, ch(“../Camera_Root/Camera_Animation”))

This maps the Interest_PositionX to the Timing of the Camera_Animation attribute. This workflow works great so far – Issues are, that there are no build in functions to set the related values and that camera and interest are not movable in the viewport. This has to be reviewed to really work with it. For those of you who are still interested in the setup – Here you can find the HIP file of the scene. Please notice that there is no related attribute for the roll, but this could be easily integrated in the current setup.