import os
from datetime import datetime
import bpy
[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_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_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, camera=None):
"""Render a frame to full_path.
Parameters
----------
frame : int
Frame to use for the render.
full_path : str
Output path with file name and extension.
camera : bpy.types.Camera (default=None)
Camera to use for the render. If None use the current scene camera.
"""
if camera:
bpy.context.scene.camera = camera
print(f"Rendering frame {frame}")
bpy.context.scene.frame_current = frame
bpy.ops.render.render()
image = bpy.data.images["Render Result"]
print(f"Saving {full_path}")
image.save_render(full_path)
[docs]def render_animation(path, start=None, end=None, camera=None):
"""Render all frames between start and end to path.
Parameters
----------
path : str
Path of the output.
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
"""
if start is None:
start = bpy.context.scene.frame_start
if end is None:
end = bpy.context.scene.frame_end
if camera:
bpy.context.scene.camera = camera
print(f"Rendering frames {start} to {end}...")
for frame in range(start, end + 1):
name = os.path.join(_timestamp_path(path), _zfill(frame, end))
render_frame(frame, name)
print("Rendering done.")