24
$\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
    Commented 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$ Commented Dec 1, 2013 at 17:37

3 Answers 3

33
$\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$ Commented 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
    Commented 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
    Commented 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
    Commented 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$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .