# Choose arbitrary vector as "up_axis" for camera with "Track To"-constraint

Hello guys!

To programmatically add a constraint to the camera in my scene, I do the following:

import bpy
from mathutils import Vector
from mathutils import Quaternion
import random

camera = bpy.data.objects.get("Camera")
target_object = bpy.data.objects.get("Cube")

camera.constraints.clear()
track_to = camera.constraints.new('TRACK_TO')
track_to.target = target_object
track_to.track_axis = 'TRACK_NEGATIVE_Z'
track_to.up_axis = 'UP_Y' # <- is it possible to change this somehow?


This works fine and the camera correctly follows the cube. However, for my task, I need the camera constraint to randomly change the selected up_axis to an arbitrary vector on the plane, which lies orthogonal to the track_axis every time I run the script. I hope Figure 1 clarifies my goal. Is there a simple way to do this?

loc, rot, scale = camera.matrix_world.decompose()
new_rot = Quaternion(rot.axis, random.uniform(-180, 180))
camera.constraints.clear() # <- seems to be required for the rotation changes to take effect
camera.rotation_quaternion = new_rot


But this approach causes the camera to entirely lose focus on the cube.

• Is the script supposed to be run for each frame? Sep 9, 2020 at 9:33
• Hello @lemon! The script will be used to generate an arbitrary amount of renderings. Every possible rotation of the camera, as shown in the figure, must be equally likely (uniform distribution), but there is no need to "animate" something or "to call the script for each frame". The workflow should be as follows: add "Track To" constraint to camera -> randomly change camera position -> randomly change rotation around "Track axis" -> take photo. This should be repeated as often as the user wants it to do so. Sep 9, 2020 at 9:50
• So do you really need the track to constraint? Why don't you align the cam to the target by the script itself? Sep 9, 2020 at 9:51
• Could do it using this approach for instance: blender.stackexchange.com/a/73013/19156 Sep 9, 2020 at 9:57
• Thank you for the tip, @lemon. The linked answer gave me an idea. This answer does the job perfectly. Sep 9, 2020 at 13:28

Align to and roll on vector.

In a related question showed the math of aligning an object to the view as defined by the camera, instead here will align the camera -Z axis to the object and roll the camera randomly around it.

• Vector v2 is the global direction camera faces

• Vector v1 is the vector defined looking at camera from object

• The rotational difference between the two gives us the rotation required to make camera point at object

• Change the space to rotate about the origin point of the object in global space.

• Make another random rotation matrix to roll about this point about the axis v1

Test script (often would use context object for cube and scene camera for camera, to avoid need to edit and run, rather simply change active object)

import bpy
from mathutils import Vector
from mathutils import Matrix
from random import random

camera = bpy.data.objects.get("Camera")
target_object = bpy.data.objects.get("Cube")

cmw = camera.matrix_world
tmw = target_object.matrix_world

v1 = tmw.translation - cmw.translation
v2 = cmw.to_3x3() @ Vector((0, 0, -1))

R = (Matrix.Translation(cmw.translation) @
v2.rotation_difference(v1).to_matrix().to_4x4() @
Matrix.Rotation(random() * radians(360), 4, v1.normalized()) @
Matrix.Translation(-cmw.translation))

camera.matrix_world = R @ cmw


Possibly worth mentioning, could do same by rolling first on v2

R = (Matrix.Translation(cmw.translation) @
Matrix.Rotation(random() * radians(360), 4, v2.normalized()) @
v2.rotation_difference(v1).to_matrix().to_4x4() @
Matrix.Translation(-cmw.translation))


Make a dolly

Another approach would be make an empty, parent camera to empty. Track the empty to the cube. Simply roll the camera using the z axis.

Examples.

https://blender.stackexchange.com/a/130456/15543

https://blender.stackexchange.com/a/176762/15543

• Thank you very much, @batFINGER. That's exactly what I needed. Sep 10, 2020 at 6:42