Source code for ddg.visualization.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, samples=64, shadows_px="128"): """Set render settings for the Eevee engine. Parameters ---------- scene : bpy.types.Scene (default=None) Scene to which to apply the render settings. If None use the current scene. samples : int (default=64) Number of samples. shadows_px : enum in ["64", "128", "256", "512", "1024", "2048", "4096"], (default="128") Number of pixels to use for rendering of shadows. """ if scene is None: scene = bpy.context.scene render = scene.render render.engine = "BLENDER_EEVEE" scene.eevee.taa_render_samples = samples scene.eevee.shadow_cube_size = shadows_px scene.eevee.shadow_cascade_size = shadows_px
[docs]def setup_cycles_renderer( scene=None, device="CPU", noise_threshold=0.01, min_samples=0, max_samples=512, time_limit=5, denoise=False, persistent_data=True, ): """Set render settings for the Cycles engine with adaptive sampling. Parameters ---------- scene : bpy.types.Scene (default=None) Scene to which to apply the render settings. If None, use the current scene. device : str (default="CPU") Cycles render device, either "CPU" or "GPU". noise_threshold : float (default=0.01) The error threshold used in adaptive sampling. min_samples : int (default=0) Minimum number of samples. max_samples : int (default=512) Minimum number of samples. time_limit : float (default=5) Maximum time of a render. Zero means no time limit. denoise : bool (default=False) Whether or not to denoise the output after render. persistent_data : bool (default=True) Whether or not to keep render data for faster re-renders and animation renders. """ if scene is None: scene = bpy.context.scene scene.render.engine = "CYCLES" scene.render.use_persistent_data = persistent_data cycles = scene.cycles cycles.device = device cycles.use_adaptive_sampling = True cycles.samples = max_samples cycles.time_limit = time_limit cycles.adaptive_min_samples = min_samples cycles.adaptive_threshold = noise_threshold cycles.use_denoising = denoise
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.")