Source code for ddg.blender.render

import os
from datetime import datetime
from enum import Enum

import bpy


[docs]class ImageFormatExtension(Enum): BMP = ".bmp" PNG = ".png" IRIS = ".sgi" JPEG = ".jpg" JPEG2000 = ".jp2" TARGA = ".tga" TARGA_RAW = ".tga" DPX = ".dpx" CINEON = ".cin" OPEN_EXR = ".exr" OPEN_EXR_MULTILAYER = ".exr" HDR = ".hdr" TIFF = ".tiff" WEBP = ".webp"
[docs]def set_world_background(color=(1, 1, 1, 1), strength=0.2, world=None): """Set the world background color and emission. Parameters ---------- color : Sequence of 4 floats between 0 and 1 (default=(1, 1, 1, 1)) Color the background will have. strength : float (default=0.2) Emission strength of the background. world : bpy.types.World (default=None) World to modify. If None, use world of the current scene. """ if world is None: world = bpy.context.scene.world world_background_shader_inputs = world.node_tree.nodes["Background"].inputs background_color = world_background_shader_inputs[0] background_strength = world_background_shader_inputs[1] background_color.default_value = color background_strength.default_value = strength
[docs]def set_film_transparency(scene=None, transparent=True): """Set the transparency of the render's film. Parameters ---------- scene : bpy.types.Scene (default=None) Scene to apply the render setup to. If None, use the current scene. transparent : bool (default=True) If True make the film transparent, else use opaque world background. """ if scene is None: scene = bpy.context.scene scene.render.film_transparent = transparent
[docs]def setup_eevee_renderer( scene=None, taa_render_samples=64, shadow_cube_size="512", shadow_cascade_size="1024", ): """Set render settings for the Eevee engine. The default arguments for every parameter other than `scene` match Blender's default settings. Parameters ---------- scene : bpy.types.Scene (default=None) Scene to which to apply the render settings. If `None` use `bpy.context.scene`. taa_render_samples : int (default=64) Number of samples per pixel for rendering. Sets `scene.eevee.taa_render_samples` to this value. shadow_cube_size : {"64", "128", "256", "512", "1024", "2048", "4096"} (default = "512") Size of point and area light shadow maps. Sets `scene.eevee.shadow_cube_size` to this value. shadow_cascade_size : {"64", "128", "256", "512", "1024", "2048", "4096"} (default = "1024") Size of sun light shadow maps. Sets `scene.eevee.shadow_cascade_size` to this value. """ # noqa: E501 # NOTE: Parameter descriptions in the docstring are taken from the Blender GUI. if scene is None: scene = bpy.context.scene render = scene.render render.engine = "BLENDER_EEVEE" scene.eevee.taa_render_samples = taa_render_samples scene.eevee.shadow_cube_size = shadow_cube_size scene.eevee.shadow_cascade_size = shadow_cascade_size
[docs]def setup_cycles_renderer( scene=None, use_persistent_data=False, device="CPU", use_adaptive_sampling=True, adaptive_threshold=0.01, samples=4096, adaptive_min_samples=0, time_limit=0, use_denoising=True, ): """Set render settings for the Cycles engine with adaptive sampling. The default arguments for every parameter other than `scene` match Blender's default settings. Parameters ---------- scene : bpy.types.Scene (default=None) Scene to which to apply the render settings. If `None`, use `bpy.context.scene`. use_persistent_data : bool (default=False) Keep render data around for faster re-renders and animation renders, at the cost of increased memory usage. Sets `scene.render.use_persistent_data` to this value. device : {"CPU", "GPU"} (default="CPU") Device to use for rendering. Sets `scene.cycles` to this value. use_adaptive_sampling : bool (default=True) Automatically reduce the number of samples per pixel based on estimated noise level. Sets `scene.cycles.use_adaptive_sampling` to this value. adaptive_threshold : float (default=0.01) Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples. Sets `scene.cycles.adaptive_threshold` to this value. samples : int (default=4096) Number of samples to render for each pixel. Sets `scene.cycles.samples` to this value. adaptive_min_samples : int (default=0) Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on noise threshold. Sets `scene.cycles.adaptive_min_samples` to this value. time_limit : float (default=0) Limit the render time (excluding synchronization time). Zero disables the limit. Measured in seconds. Sets `scene.cycles.time_limit` to this value. use_denoising : bool (default=True) Denoise the rendered image. Sets `scene.cycles.use_denoising` to this value. """ # NOTE: Parameter descriptions in the docstring are taken from the Blender GUI. if scene is None: scene = bpy.context.scene scene.render.engine = "CYCLES" if use_persistent_data is not None: scene.render.use_persistent_data = use_persistent_data cycles = scene.cycles cycles.device = device cycles.use_adaptive_sampling = use_adaptive_sampling cycles.samples = samples cycles.time_limit = time_limit cycles.adaptive_min_samples = adaptive_min_samples cycles.adaptive_threshold = adaptive_threshold cycles.use_denoising = use_denoising
def _timestamp_path(path): """Get path to a timestamp subdirectory under path. Parameters ---------- path : str Prefix path to which to append a timestamp subdir. Returns ------- str """ timestamp = format(datetime.now(), "%y%m%d-%H%M") return os.path.join(path, timestamp, "")
[docs]def set_render_stamp_note( note, scene=None, font_size=26, font_color=(0, 0, 0, 1), bg_color=(0, 0, 0, 0), ): """Set a custom string to display in the render stamp. Parameters ---------- note : str The string to display in render stamp. scene : bpy.types.Scenes (default=None) The scene to setup render stamp. If None use the current scene. font_size : int (default=26) The font size of displayed string. font_color : Iterable of four floats (default=(0.,0.,0.,1.)) RGBA values of displayed string. bg_color : Iterable of four floats (default=(0.,0.,0.,0.)) RGBA values of background of displayed string. Notes ----- In order to display a stamp note `scene.render.use_stamp` must be set to `True`. Blender then automatically adds other render stamps, such as render time, date and file name. You can disable those manually, for example by setting `scene.render.use_stamp_filename = False`. For more settings, see `bpy.types.RenderSettings`. """ if scene is None: scene = bpy.context.scene render = scene.render render.use_stamp = True render.use_stamp_note = True render.stamp_note_text = note render.stamp_font_size = font_size render.stamp_background = bg_color render.stamp_foreground = font_color
[docs]def set_render_output_images( output_path, time=True, scene=None, file_format="PNG", alpha=True, quality=100, ): """Set the rendering output for an image sequence. Unless time=False, each time this function is called a new timestamp is set, and the output images will be saved under {output_path}/{timestamp}/ in order to isolate renders between sessions. Parameters ---------- output_path : str The directory path to which to save to ouput. time : bool (default=True) Whether or not to create a timestamp subdirectory and save the rendered images \ under it. scene : bpy.types.Scenes (default=None) The scene to which to apply the settings. If None use the current scene. file_format : str (default="PNG") The file format of the images. In upper letters. For example 'PNG' or 'JPEG2000' alpha : bool (default=True) Whether to use alpha in the output image. quality : int (default=100) Quality of the output image in percent. See Also -------- render_frame, render_animation """ if scene is None: scene = bpy.context.scene render = scene.render render.image_settings.file_format = file_format if alpha: render.image_settings.color_mode = "RGBA" else: render.image_settings.color_mode = "RGB" render.image_settings.quality = quality render.filepath = ( _timestamp_path(output_path) if time else os.path.join(output_path, "") )
def _zfill(frame, end_frame): """Format frame with enough prefix zeros to match end_frame. Parameters ---------- frame : int Frame number to format. end_frame : int Reference frame. """ return str(frame).zfill(len(str(end_frame)))
[docs]def render_frame(frame, full_path=None, camera=None): """Render a frame to full_path. Parameters ---------- frame : int Frame to use for the render. full_path : str (default=None) Full output path with file name and extension. If None use the current scene `scene.render.filepath` and `scene.render.image_settings.file_format`. camera : bpy.types.Camera (default=None) Camera to use for the render. If None use the current scene camera. See Also -------- set_render_output_images """ scene = bpy.context.scene if camera: scene.camera = camera print(f"Rendering frame {frame}") scene.frame_current = frame bpy.ops.render.render() image = bpy.data.images["Render Result"] if full_path is None: output_directory = scene.render.filepath image_format = scene.render.image_settings.file_format full_path = os.path.join( output_directory, str(frame) + ImageFormatExtension[image_format].value ) print(f"Saving {full_path}") image.save_render(full_path)
[docs]def render_animation(output_directory=None, start=None, end=None, camera=None): """Render all frames between start and end to path. Parameters ---------- output_directory : str (default=None) Directory of the render output. If None use current scene `scene.render.filepath` and `scene.render.image_settings.file_format`. start : int (default=None) Start frame. If None use current scene start frame. end : int (default=None) End frame. If None use current scene end frame. camera : bpy.types.Camera (default=None) Camera to use for the render. If None, use the current scene camera. See Also -------- set_render_output_images """ scene = bpy.context.scene if output_directory is None: output_directory = scene.render.filepath if start is None: start = scene.frame_start if end is None: end = scene.frame_end if camera: scene.camera = camera print(f"Rendering frames {start} to {end}...") image_format = scene.render.image_settings.file_format for frame in range(start, end + 1): path = os.path.join( output_directory, _zfill(frame, end) + ImageFormatExtension[image_format].value, ) render_frame(frame, full_path=path) print("Rendering done.")