I created the appended test scene, to investigate an issue in a much more complex scene I have. “Luckily” the issue appears also in this small recreation of the basic principles of the scene which I tried to solve with a python script in a “frame_change_pre” handler (code in the blend file).

The rendered video is here: video of issue

If you watch the video then you see a green ball appear between Suzanne and the camera. The green balls position is determined by the python script in the frame_change_pre handler and it should depend on the camera position and the position of a keyframed empty. At certain frames I want the ball to be between Suzanne and the camera. Basically, the script does what I want.

However, if you watch the video carefully, you see some jumps of the ball in certain frames, instead of a continuous smooth movement. I observed, that I sometimes have differences in the location of the ball in the rendered image compared to the viewport location of the ball. By this I mean, if I increase the frame number by manually clicking the up arrow of the “Current Frame” GUI control element, I don’t see these glitches that I observe in the render.

However, if I once click the down arrow in the “Current Frame” control, I do see the ball jumping from one side to the other in the viewport (only when I use the _pre handler, by using _post, everything is even better by going back and forth). This suggests, that the render mechanism in Blender does frame changes also in the backward direction during animation rendering?!? Is that correct and why is that so? What can I do about this effect in order to disappear?

Thanks, Maik

Here is the code (exactly as is from the uploaded blend file, meanwhile I tweaked my local source already and have the handler.clear() inside the script, so that I can safely overwrite the handler) (I also realiesed that this "hidden_spaceship" object is just pointless... but taking it out does not solve the issue with the position glitch of the ball):

import bpy
import bmesh
import mathutils

def create_vector_line(start, end, name):
    mesh = bpy.data.meshes.new(name=name)
    obj = bpy.data.objects.new(name, mesh)

    bpy.context.view_layer.objects.active = obj

    mesh = bpy.context.object.data
    bm = bmesh.new()

    v1 = bm.verts.new(start)
    v2 = bm.verts.new(end)
    bm.edges.new([v1, v2])

    return obj  # return the created object for later updates

def update_vector_line(obj, start, end):
    bm = bmesh.new()

    bm.verts[0].co = start
    bm.verts[1].co = end


def lerp(start, end, t):
    return start + (end - start) * t

# Initialize global variable outside of the function
starting_lambda_value = None

def adjust_spaceship_position3(scene):
    global starting_lambda_value # Initialize this outside the if-elif blocks
    print("Test function called.")
    current_frame = scene.frame_current
    print("Current Frame:", current_frame)

    frames_before_flaw = 20
    frames_after_flaw = 140
    frames_overtake = 251

    # Get the total number of frames in the animation
    total_frames = scene.frame_end

    camera_obj = bpy.data.objects['Camera']
    flaw = bpy.data.objects['Empty'].location
    spaceship = bpy.data.objects['Icosphere_scripted']
    hidden_spaceship = bpy.data.objects['Cube_Dummy']

    camera = camera_obj.matrix_world.to_translation()
    print("Camera Location:", camera)
    print("Flaw Location:", flaw)

    direction_camera_flaw = (flaw - camera).normalized()
    print("Direction Camera Flaw:", direction_camera_flaw)

    lambda_value = 0.0
    original_position = hidden_spaceship.matrix_world.to_translation()
    print("Original Position (hidden):", original_position)

    new_position = None  # Initialize new_position

    if current_frame < frames_before_flaw:
        spaceship.location = camera + mathutils.Vector((0.0, 0.0, -1.0))
    elif frames_before_flaw <= current_frame < frames_after_flaw:
        t = (current_frame - frames_before_flaw) / (frames_after_flaw - frames_before_flaw)
        lambda_value = lerp(0.5, 0.1, t)
        new_position = lambda_value * flaw + (1 - lambda_value) * camera
        starting_lambda_value = lambda_value
    elif frames_after_flaw <= current_frame < frames_overtake:
        t = (current_frame - frames_after_flaw) / (frames_overtake - frames_after_flaw)
        lambda_value = lerp(starting_lambda_value, 0.1, t)
        new_position = lambda_value * flaw + (1 - lambda_value) * camera
        starting_lambda_value = lambda_value
    elif current_frame >= frames_overtake:
        # Interpolate lambda values to make spaceship accelerate towards the camera
        t = (current_frame - frames_overtake) / (total_frames - frames_overtake)
        lambda_value = lerp(starting_lambda_value, 0.1, t)  # Starts at 0.9 and goes to 0.1, making it go towards the camera
        new_position = lambda_value * flaw + (1 - lambda_value) * camera

    print("Lambda Value:", lambda_value)
    # Update or create vector lines
    global direction_line  # Make it global to remember it across function calls
    if 'direction_line' not in globals():
        direction_line = create_vector_line(camera, flaw, 'Direction_Camera_Flaw')
        update_vector_line(direction_line, camera, flaw)

    spaceship.location = new_position
    print("New position (visible):", new_position)

# Attach the function to frame changes
# Register the handler
  • $\begingroup$ Hello ! Some people like me are a bit hesitant to download and execute code from random blend files on the internet. Would you mind posting your code inside your question as text so we can take a look at it ? Cheers $\endgroup$
    – Gorgious
    Sep 29, 2023 at 13:01
  • $\begingroup$ Hmmm, I see.... sorry for that. Put it into the post above. $\endgroup$
    – Kiamur
    Sep 29, 2023 at 18:45


You must log in to answer this question.

Browse other questions tagged .