This code will randomly place the camera around an object, and point at it, for each frame of an animation. The problem is that sometimes it doesn't seem to be pointing directly at the center.

enter image description here

import bpy
import math
import mathutils
import time

from random import randint

def eraseAllKeyframes(scene,passedOB = None):

    if passedOB != None:

        ad = passedOB.animation_data

        if ad != None:
            print ('ad=',ad)


def look_at(obj_camera, point):
    loc_camera = obj_camera.matrix_world.to_translation()
    direction = point - loc_camera
    # point the cameras '-Z' and use its 'Y' as up
    rot_quat = direction.to_track_quat('-Z', 'Y')
    # assume we're using euler rotation
    obj_camera.rotation_euler = rot_quat.to_euler()

scene = bpy.data.scenes["Scene"]

passedOB =  bpy.context.scene.objects.active
eraseAllKeyframes(scene,passedOB = None)

cp = mathutils.Vector((0.0, 0.0, 0.0))
cam = bpy.data.objects['Camera']

scene.frame_start = 1
scene.frame_end = 10

for i in range(10):


  x, y, z = randint(4,10), randint(4,10), randint(8,10)
  cam.location = (x, y, z)
  bpy.data.objects['Camera'].keyframe_insert('location', frame=i)

  look_at(cam, cp)
  bpy.data.objects['Camera'].keyframe_insert('rotation_euler', frame=i)


scene.render.filepath = "E:\\testing"
scene.render.image_settings.file_format = "JPEG"

So.. I don't know why, but the solution that worked for me was to split the keyframe assignment into two separate loops.

Investigations made it appear as though the 'camera look' function just wasn't being called.. "in time?" I'm not sure. Working code below.

for i in range(10):

  x, y, z = randint(6,15), randint(-6,16), randint(6,35)
  cam.location = (x, y, z)

for i in range(10):

  look_at(cam, cp)
|improve this answer|||||
  • 2
    $\begingroup$ Cool. Didn't get around to commenting the other day. If you are using <= 2.79x a scene update may be needed (end of lookat) to update the object matrices. (Or build and set the world matrix instead, since you have the translation and rotation) Suggest using a track to constraint camera to object of interest Would then need only to keyframe camera location. (Or set via frame change handler) Re the question code, eraseAllKeyframes(scene, passedOB) will make sure the animation data is cleared, as it is will not. $\endgroup$ – batFINGER Jan 21 at 7:44

Frame change handler and track to constraint.

As commented, another way to do this would be to

  • Set up a track to constraint camera to object. Tracking the point of view, its negative z axis, y axis up, to the object of interest.
  • Use a frame change handler to set the random camera location. The scene is passed as argument to handler. The rendering camera is scene.camera

Test script below using both concepts.

enter image description here Render result with suzanne masquerading as cube in default file

import bpy
from random import randint
from bpy.app.handlers import frame_change_pre

context = bpy.context

scene = context.scene
ob = context.object
cam = scene.camera

if ob and cam:
    tt = cam.constraints.get("OB_Track") or cam.constraints.new('TRACK_TO')
    tt.name = "OB_Track"
    tt.target = ob
    tt.up_axis = 'UP_Y'
    tt.track_axis = 'TRACK_NEGATIVE_Z'

def frame_change(scene):
    cam = scene.camera
    if cam:
        cam.location = (randint(2, 10), 
                        randint(2, 10), 
                        randint(8, 10))

# clear previous

funcs = [f for f in frame_change_pre if f.__name__ == "frame_change"]
while funcs:
# set up the frame change handler


Of course if you wish to keep these random locations, keyframe only camera locations as in question code instead.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.