This Python script is designed to automate a series of animations and renders within Blender, a popular 3D creation suite. Initially, the script sets up an orthographic camera, placing it at a specific location above the world origin and angling it downwards. The camera is parented to an empty object positioned at the world origin, facilitating orbital movement later on. The script also prepares the render settings, specifically configuring it to output images as transparent PNGs of 512x512 pixels.
The meat of the script involves cycling through each animation tied to a target object, playing them frame-by-frame and rendering each frame as a separate image file. These files are saved in neatly organized folders, named according to the animation and the frame number. After each set of animations is rendered, the script rotates the camera (or more precisely, the parent empty object) around the Z-axis by 45 degrees. This process is repeated 8 times, ensuring all angles are captured. To manage the pace of the animations, a global FRAME_RATE variable is used, allowing easy adjustments to the frames per second. All in all, this script serves as a convenient tool to generate a comprehensive set of renderings for 3D animations from multiple perspectives.
import bpy
import os
CAMERA_ANGLE_RADS = -0.5
OUTPUT_PATH = "<output folder>"
FRAME_RATE = 24
def setup_camera():
# Delete all existing cameras
for obj in bpy.data.objects:
if obj.type == 'CAMERA':
bpy.data.objects.remove(obj)
if obj.type == 'EMPTY':
bpy.data.objects.remove(obj)
# Create a new camera
cam = bpy.data.cameras.new("OrthoCam")
cam.type = 'ORTHO'
# Create a new object with the camera
camera_object = bpy.data.objects.new("OrthoCam", cam)
bpy.context.collection.objects.link(camera_object)
# Create an empty object at world origin
empty_obj = bpy.data.objects.new("Empty", None)
bpy.context.collection.objects.link(empty_obj)
empty_obj.location = (0.0, 0.0, 0.0) # world origin
# Parent the camera to the empty object
camera_object.parent = empty_obj
# Set the camera as the active camera
bpy.context.scene.camera = camera_object
# Move camera away from the origin and make it look at the origin
camera_object.location = (0.0, -10.0, 0.0)
camera_object.rotation_euler = (1.7707963267948966, 0, 0)
#look_at(camera_object, Vector((0,0,0)))
empty_obj.rotation_euler = (CAMERA_ANGLE_RADS,0,0)
return camera_object, empty_obj # return both the camera and the empty object
def look_at(obj, target):
direction = target - obj.location
rot_quat = direction.to_track_quat('-Z', 'Y')
obj.rotation_euler = rot_quat.to_euler()
def set_render_settings(resolution, output_path):
bpy.context.scene.render.image_settings.file_format = 'PNG'
bpy.context.scene.render.image_settings.color_mode = 'RGBA' # RGBA for transparency
bpy.context.scene.render.film_transparent = True
bpy.context.scene.render.resolution_x = resolution
bpy.context.scene.render.resolution_y = resolution
bpy.context.scene.render.filepath = output_path
def play_and_render_animations(target_obj, output_folder):
for action in bpy.data.actions:
# Set current action as the target object's animation
target_obj.animation_data.action = action
bpy.context.scene.render.fps = FRAME_RATE # se
# Create subfolder for current animation
animation_folder = os.path.join(output_folder, action.name)
os.makedirs(animation_folder, exist_ok=True)
# Render each frame of the animation
for frame in range(int(action.frame_range[0]), int(action.frame_range[1]) + 1):
bpy.context.scene.frame_set(frame)
bpy.context.scene.render.filepath = os.path.join(animation_folder, str(frame))
bpy.ops.render.render(write_still=True)
def rotate_camera(camera_object, angle):
camera_object.rotation_euler[2] += angle
# Script entry point
def main():
# Output folder
os.makedirs(OUTPUT_PATH, exist_ok=True)
# Target object to animate
target_obj = bpy.context.object
# Camera setup
camera_object, empty_object = setup_camera()
# Render settings
set_render_settings(512, OUTPUT_PATH)
# Rotate camera, play and render animations
for i in range(8):
play_and_render_animations(target_obj, OUTPUT_PATH+"\\direction_"+str(i))
rotate_camera(empty_object, 0.785398) # 45 degrees in radians
if __name__ == "__main__":
main()
After running it, you use Sprite Combinder to create sprite sheets from all the renders