22
$\begingroup$

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?

$\endgroup$
2
  • $\begingroup$ do you want to set the cameras direction in the game engine? $\endgroup$
    – stacker
    Dec 1, 2013 at 17:35
  • $\begingroup$ No, I'm writing a script to create a scene with the camera in a specific location. $\endgroup$ Dec 1, 2013 at 17:37

3 Answers 3

31
$\begingroup$

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())
$\endgroup$
4
  • 2
    $\begingroup$ 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? $\endgroup$ Dec 2, 2013 at 18:22
  • 3
    $\begingroup$ 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. $\endgroup$
    – Chuck
    Nov 23, 2016 at 23:59
  • 2
    $\begingroup$ You might want to call bpy.context.scene.update() after changing the camera's location to make sure the matrix is up to date. $\endgroup$
    – Daerst
    Mar 26, 2019 at 10:15
  • $\begingroup$ +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. $\endgroup$
    – Arman
    Apr 26, 2022 at 21:31
10
$\begingroup$

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

$\endgroup$
4
$\begingroup$

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))
$\endgroup$

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.