Helloooo!
It’s been a month since the first post on this project, and I’ve been hard at work. According to my spreadsheet, I have finished 40% of the animation tasks I have originally planned for this film.
My plans may change drastically, as I have come up with a way to cut the work in half AND make a much more interesting story out of this film. It’s a bit of a gamble, but I think it’ll pay off! What I am talking about, you will have to wait to see. :P
My deadline is going to be somewhere around the 15-19th of June, so that’s around when you can expect me to publish this… maybe. I don’t know if the version I’ll be presenting at my exam will be the one to be published…
I’ve been chipping away at all the animation I need to draw. For the portion I need to try out the new idea I have, I just have 3 shots left to animate.
Sheepit!
Due to my poor optimization of my projects, my integrated graphics laptop was really starting to struggle with rendering after a certain point. Enter Sheepit! It’s a distributed network of computers ready to help you render your projects, the service is gratis (you just need to help other users render 10 frames with your computer in order to be able to use it), and the client and server scripts have open source code. I highly recommend checking it out and perhaps even joining the community. If you’d like to help out me personally with rendering stuff, you can rquest to join my team on there :)
Optimization…
I did in fact find out what was dragging down the performance so badly.
“Due to lack of global cache at the moment, each Line Art modifier will run the entire occlusion calculation for itself. So if you have multiple Line Art modifiers to select different parts of the scene (to apply different styles, etc.), the evaluation will take much longer. There are plans to remedy this in the future, but this is a known limitation for now.” - Line Art Modifier, Blender 5.1 Manual
I had created like… 5 Line Art modifiers. Each running an expensive occlusion calculation on every single frame. Oops.
I consolidated all of them into just one object with just one modifier.
This is not the end of my problems with this specific modifier, though… Sheepit’s rendering scripts don’t support the modifier at all. There is a fix for this, though. I can create keyframes on the Line Art object for every single frame, then hit “Apply to all keyframes”, and the Line Art is “baked” in, no longer a modifier. I even wrote a little script to automate this! Additionally, it also removes duplicate frames, for when nothing changes in frame and thus the Line Art doesn’t need more keyframes. To optimize the file.
import bpy
import os
# your lineart greasepencil object
GP_OBJECT_NAME = "INSERT_OBJECT_NAME"
#get the gp object
gp_obj = bpy.data.objects.get(GP_OBJECT_NAME)
if gp_obj is None:
raise Exception(f"Grease Pencil object '{GP_OBJECT_NAME}' not found.")
if gp_obj.type != 'GREASEPENCIL':
raise Exception(f"Object '{GP_OBJECT_NAME}' is not a Grease Pencil object.")
lineart_mod = None
for mod in gp_obj.modifiers:
if mod.type == 'LINEART':
lineart_mod = mod
break
if lineart_mod is None:
raise Exception("No Line Art modifier found.")
scene = bpy.context.scene
start_frame = scene.frame_start
end_frame = scene.frame_end
gp_data = gp_obj.data
print(f"Creating keyframes from {start_frame} to {end_frame}...")
for frame in range(start_frame, end_frame + 1):
scene.frame_set(frame)
for layer in gp_data.layers:
existing = None
for fr in layer.frames:
if fr.frame_number == frame:
existing = fr
break
if existing is None:
layer.frames.new(frame)
print("Finished generating GP keyframes.")
bpy.context.view_layer.objects.active = gp_obj
gp_obj.select_set(True)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.modifier_apply(
modifier=lineart_mod.name,
all_keyframes=True
)
print("Line Art modifier applied.")
#remove duplicate frames
def frames_are_identical(frame_a, frame_b, tolerance=0.0001):
# Different drawing counts
if len(frame_a.drawing.strokes) != len(frame_b.drawing.strokes):
return False
for stroke_a, stroke_b in zip(frame_a.drawing.strokes,
frame_b.drawing.strokes):
# Different point counts
if len(stroke_a.points) != len(stroke_b.points):
return False
for pt_a, pt_b in zip(stroke_a.points, stroke_b.points):
# Compare positions
if (pt_a.position - pt_b.position).length > tolerance:
return False
return True
print("Removing duplicate frames...")
for layer in gp_data.layers:
frames_to_delete = []
sorted_frames = sorted(
layer.frames,
key=lambda f: f.frame_number
)
previous = None
for frame in sorted_frames:
if previous is not None:
if frames_are_identical(previous, frame):
frames_to_delete.append(frame)
previous = frame
for frame in reversed(frames_to_delete):
print(f"Deleting duplicate frame {frame.frame_number}")
layer.frames.remove(frame.frame_number)
#save new file
current_path = bpy.data.filepath
directory = os.path.dirname(current_path)
filename = os.path.basename(current_path)
name, ext = os.path.splitext(filename)
new_filename = f"{name}_lineartbaked{ext}"
new_path = os.path.join(directory, new_filename)
bpy.ops.wm.save_as_mainfile(filepath=new_path)
print(f"Saved baked file to:n{new_path}")
Look ma, I’m scripting with the Blender API for the first time in my life!
Conclusion
Yeah, sorry, no new visual material this time. You’ll have to wait and see. I don’t feel like uploading any tiny snippets…
First 2 scenes are practically finished. Just 1 left to polish, and another one needs a bit more work than that.
And I’ll have to start working on the surprise twist I’ve hinted at in the beginning of this post. 1 month left… I think I can do it.
And then I also have to make a behind-the-scenes type video essay, and then a written thesis…………
Deep breaths, deep breaths…

