24

I'm trying to use Python to place a camera. I know the camera's location, and the forward and up vectors for the camera (i.e. the direction it's pointing and its orientation). How do I do this?

I know that it's possible to calculate a raw world matrix from the above information, which I can then apply to the camera, but I was rather hoping not to have to. Is there an easier way?

David Given
  • 562
  • 1
  • 3
  • 12

3 Answers3

33

Heres a script to make a camera point towards any point in space.

import bpy

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()

# Test
obj_camera = bpy.data.objects["Camera"]
obj_other = bpy.data.objects["Cube"]

obj_camera.location = (5.0, 2.0, 3.0)
look_at(obj_camera, obj_other.matrix_world.to_translation())
ideasman42
  • 47,387
  • 10
  • 141
  • 223
  • 2
    I'm afraid I don't understand this --- the docs for to_track_quat() are... brief. I don't see how the fixed axis parameters get rotated to point in my desired direction. (When I say I have the location and forward and up vectors, I mean literally I have a Point and two Vector objects.) Can you expand? – David Given Dec 02 '13 at 18:22
  • 3
    The camera starts off pointing along the -Z axis with the top of the camera pointing along the +Y axis. The variable "direction" is the vector from the camera to the point. The function direction.to_track_quat('-Z', 'Y') returns the quaternion that rotates '-Z' so that it aligns with the direction vector. This rotation is not unique because the rotated camera can still rotate about direction vector. Specifying Y gives the rotation quaternion with the -Z vector aligned with the direction vector and the Y vector pointing up. – Chuck Nov 23 '16 at 23:59
  • 2
    You might want to call bpy.context.scene.update() after changing the camera's location to make sure the matrix is up to date. – Daerst Mar 26 '19 at 10:15
  • +1 for @Daerst 's suggestion to update the scene, this fixed things for me. However, in Blender 2.8+ you'll need bpy.context.view_layer.update() instead. – Arman Apr 26 '22 at 21:31
10

Here is a version of ideasman42's look_at function that also allows you to roll the camera (or any object) about the axis from camera to target:

def point_at(obj, target, roll=0):
    """
    Rotate obj to look at target
:arg obj: the object to be rotated. Usually the camera
:arg target: the location (3-tuple or Vector) to be looked at
:arg roll: The angle of rotation about the axis from obj to target in radians. 

Based on: https://blender.stackexchange.com/a/5220/12947 (ideasman42)      
"""
if not isinstance(target, mathutils.Vector):
    target = mathutils.Vector(target)
loc = obj.location
# direction points from the object to the target
direction = target - loc
tracker, rotator = (('-Z', 'Y'),'Z') if obj.type=='CAMERA' else (('X', 'Z'),'Y') #because new cameras points down(-Z), usually meshes point (-Y)
quat = direction.to_track_quat(*tracker)

# /usr/share/blender/scripts/addons/add_advanced_objects_menu/arrange_on_curve.py
quat = quat.to_matrix().to_4x4()
rollMatrix = mathutils.Matrix.Rotation(roll, 4, rotator)

# remember the current location, since assigning to obj.matrix_world changes it
loc = loc.to_tuple()
#obj.matrix_world = quat * rollMatrix
# in blender 2.8 and above @ is used to multiply matrices
# using * still works but results in unexpected behaviour!
obj.matrix_world = quat @ rollMatrix
obj.location = loc

It can be used like this:

import math
cube = bpy.data.objects["Cube"]
cube.location = (5, -5, 5)
cam = bpy.data.objects["Camera"]
cam.location = (5, -5, -2)
point_at(cam, cube.location, roll=math.radians(45))

enter image description here

Sadern Alwis
  • 159
  • 2
  • 8
unutbu
  • 350
  • 3
  • 11
4

One way is to assign tuples directly to the camera object's location and rotation_euler attributes. For example, with the camera selected:

import bpy
from math import radians

camera = bpy.context.object
camera.location = (1.0, 0.0, 1.0)
camera.rotation_euler = (radians(45), 0.0, radians(30))
Adhi
  • 14,310
  • 1
  • 55
  • 62