Blender Sprite Renderer

Aug 01, 2023

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

gamedevblender
Creative

Yasen Dinkov

Automating Azure with Azure Automation (Accounts)

Trimming stale branches from Azure DevOps