Camera: Keep perseptive by changing focal length?

is there a possibility to synchronize the camera position to the focal lenght to keep the perspective? Let me say, when I change the focal lenght of the camera from 50 to 80 the image detail is changed by zooming in. So, to compansate this zoom in I have to reposition the camera manually by it's default's local z axis. Any Idea how to do that?

• It sounds like you are describing this effect youtube.com/watch?v=ql18VmQnB48 The camera movement that you need will depend on the distance to your object, so I'm not sure if there is an easy automatic way to do this. – Matt Sep 25 '20 at 23:12
• The word should be 'Keep frame/ object size when changing focal length/ perspective'. – MA Jacob Sep 26 '20 at 17:10
• I don't know if this camera zoom rig still works ti was designed to do the famous dolly-zoom effect that you are looking for: blendswap.com/blend/8285 – susu Sep 26 '20 at 17:44

If

• d1 is the distance to the object viewed by the camera with a focal length f1
• d2 is the wanted distance when the focal length is f2

Then:

d1 / f1 = d2 / f2


So that:

d2 = f2 * d1 / f1


If you want to focus on an object, you need to drive the camera on its local Z axis considering this distance shift. So we need to get the distance at the starting point and calculate the new position when the focal length is changed.

A way to do it in Blender is to use drivers.

As we want to keep the original camera position (which corresponds to d1), we can use delta transforms to do it.

These drivers will use some Python functions:

import bpy
from mathutils import Vector

def calculate_delta(self, initial_focal_length):
cube_location = bpy.data.objects['Cube'].location
location = self.location
distance = (location - cube_location).length
focal_lens = self.data.lens
delta = distance - (focal_lens * distance / initial_focal_length)
return delta

def in_world(self, delta):
matrix = self.matrix_world.copy()
matrix[0][3] = 0
matrix[1][3] = 0
matrix[2][3] = 0
return (matrix @ Vector((0, 0, -1))) * delta

def move_delta(self, initial_focal_length):
delta = calculate_delta(self, initial_focal_length)
return in_world(self,delta)

bpy.app.driver_namespace["move_delta"] = move_delta


The script calculates the delta from the given formula, translates it in world space to shift the camera. And returns the wanted vector. Then, it binds the function to the driver namespace.

Run this script once in order to use it in drivers.

Attach a driver to each component of the delta location:

The drivers use the function giving self (the camera), the initial focal length (50) and get the corresponding componant: x, y or z.

Note: the cube which is focused on in this example has its origin placed at the front plane.