I am writing a script to procedurally generate images of a target object on a terrain. The basic logic I am thinking of is to have the camera directly above the target pointing straight down, apply some XYZ offsets so that the target object is not exactly centred, then rotate the camera around the target (by a random amount from zero to a specified maximum rotation value, keeping the offsets, and keeping the direction of the camera relative to the target)

This has to be all in python scripting, as it will be running headless without the GUI. This function will be called multiple times to generate images, with the target object moving every time, so needs to reset the camera position & orientation, apply the offsets, then rotate.

Here is a dummy version of what I have so far, using the default cube & camera.

    import bpy
    from random import uniform
    from mathutils import Matrix, Euler
    import math

    def add_jitter(base_value: int = None, jitter: int = None) -> int:
        helper function to add random jitter to camera position
       jittered_value = base_value + uniform(-jitter, jitter)
       return jittered_value

    def place_camera(
        cam_obj: object = None,
        cam_height: int = None,
        xy_jitter: int = None,
        height_jitter: int = None,
        rotation_bound: int = None,
        target: object = None,
     ) -> None:

        # Reset camera orientation (straight down)
        cam_obj.rotation_euler = (0, 0, 0)

        # Add random jitter to xyz coordinates
        height_jittered = add_jitter(target.location.z + cam_height, height_jitter)
        x_jittered = add_jitter(target.location.x, xy_jitter)
        y_jittered = add_jitter(target.location.y, xy_jitter)

        # Generate random rotations to apply
        theta_x = math.radians(uniform(0, rotation_bound))
        theta_y = math.radians(uniform(0, rotation_bound))
        theta_z = math.radians(uniform(0, 360))

        # Generate translation & rotation matrices, apply sequentially to camera
        T = Matrix.Translation((x_jittered, y_jittered, height_jittered))
        R = Euler((theta_x, theta_y, theta_z), "XYZ").to_matrix()
        mat = R.to_4x4() @ T
        cam_obj.matrix_world = mat

    cam_obj = bpy.context.scene.objects["Camera"]
    target = bpy.context.scene.objects["Cube"]
    camera_height = 10
    xy_jitter = 2
    height_jitter = 2
    max_rotation = 45
    place_camera(cam_obj, camera_height, xy_jitter, height_jitter, max_rotation, target)

However, this does not seem to do the translation & rotation around the target object as intended, I get strange camera placements and orientations that dont face the target. I am relatively new to scripting in blender, and very new to matrix transformations, so its very possible I'm applying my rotations & transform matrices incorrectly

  • $\begingroup$ Hello ! Shouldn't cam_obj.rotation_euler = (0, 0, -1) be cam_obj.rotation_euler = (0, 0, 0) ? Are you by any chance using a generative language model like chatGPT to write your script ? $\endgroup$
    – Gorgious
    Commented May 24, 2023 at 13:02
  • $\begingroup$ No I didnt use chatGPT, why do you ask? The initial rotation is (0, 0, -1) so that the camera points directly downwards to begin with, as explained in the text $\endgroup$
    – oscr104
    Commented May 24, 2023 at 13:13
  • $\begingroup$ Ah I see, you're correct that (0, 0, 0) would be neutral orientation. The angle relative to the z axis is more important than the z rotation, so I hadn't picked up on this. Edited to reflect this. $\endgroup$
    – oscr104
    Commented May 24, 2023 at 13:20
  • $\begingroup$ Because the syntax of the code looks as an output out of chatGPT, and having this information may be helpful in solving your problem. Have you tested it in the 3D viewport ? A rotation of -1 radians is approximately -57,3° around the Z axis;, so it does point downwards but at a random angle $\endgroup$
    – Gorgious
    Commented May 24, 2023 at 13:20
  • $\begingroup$ You should convert all your angles to radians using math.radians, trigonometric operations use radians instead of degrees $\endgroup$
    – Gorgious
    Commented May 24, 2023 at 13:22


You must log in to answer this question.

Browse other questions tagged .