def enable_diffuse_color_output(output_dir: Optional[str] = None, file_prefix: str = "diffuse_", output_key: str = "diffuse"): """ Enables writing diffuse color (albedo) images. Diffuse color images will be written in the form of .png files during the next rendering. :param output_dir: The directory to write files to, if this is None the temporary directory is used. :param file_prefix: The prefix to use for writing the files. :param output_key: The key to use for registering the diffuse color output. """ if output_dir is None: output_dir = Utility.get_temporary_directory() bpy.context.scene.render.use_compositing = True bpy.context.scene.use_nodes = True tree = bpy.context.scene.node_tree links = tree.links bpy.context.view_layer.use_pass_diffuse_color = True render_layer_node = Utility.get_the_one_node_with_type(tree.nodes, 'CompositorNodeRLayers') final_output = render_layer_node.outputs["DiffCol"] output_file = tree.nodes.new('CompositorNodeOutputFile') output_file.base_path = output_dir output_file.format.file_format = "PNG" output_file.file_slots.values()[0].path = file_prefix links.new(final_output, output_file.inputs['Image']) Utility.add_output_entry({ "key": output_key, "path": os.path.join(output_dir, file_prefix) + "%04d" + ".png", "version": "2.0.0" })
def enable_distance_output(output_dir: Optional[str] = None, file_prefix: str = "distance_", output_key: str = "distance", distance_start: float = 0.1, distance_range: float = 25.0, distance_falloff: str = "LINEAR"): """ Enables writing distance images. Distance images will be written in the form of .exr files during the next rendering. :param output_dir: The directory to write files to, if this is None the temporary directory is used. :param file_prefix: The prefix to use for writing the files. :param output_key: The key to use for registering the distance output. :param distance_start: Starting distance of the distance, measured from the camera. :param distance_range: Total distance in which the distance is measured. \ distance_end = distance_start + distance_range. :param distance_falloff: Type of transition used to fade distance. Available: [LINEAR, QUADRATIC, INVERSE_QUADRATIC] """ if output_dir is None: output_dir = Utility.get_temporary_directory() bpy.context.scene.render.use_compositing = True bpy.context.scene.use_nodes = True GlobalStorage.add("renderer_distance_end", distance_start + distance_range) tree = bpy.context.scene.node_tree links = tree.links # Use existing render layer render_layer_node = Utility.get_the_one_node_with_type(tree.nodes, 'CompositorNodeRLayers') # Set mist pass limits bpy.context.scene.world.mist_settings.start = distance_start bpy.context.scene.world.mist_settings.depth = distance_range bpy.context.scene.world.mist_settings.falloff = distance_falloff bpy.context.view_layer.use_pass_mist = True # Enable distance pass # Create a mapper node to map from 0-1 to SI units mapper_node = tree.nodes.new("CompositorNodeMapRange") links.new(render_layer_node.outputs["Mist"], mapper_node.inputs['Value']) # map the values 0-1 to range distance_start to distance_range mapper_node.inputs['To Min'].default_value = distance_start mapper_node.inputs['To Max'].default_value = distance_start + distance_range final_output = mapper_node.outputs['Value'] # Build output node output_file = tree.nodes.new("CompositorNodeOutputFile") output_file.base_path = output_dir output_file.format.file_format = "OPEN_EXR" output_file.file_slots.values()[0].path = file_prefix # Feed the Z-Buffer or Mist output of the render layer to the input of the file IO layer links.new(final_output, output_file.inputs['Image']) Utility.add_output_entry({ "key": output_key, "path": os.path.join(output_dir, file_prefix) + "%04d" + ".exr", "version": "2.0.0", "trim_redundant_channels": True })
def render(output_dir: Optional[str] = None, file_prefix: str = "rgb_", output_key: Optional[str] = "colors", load_keys: Optional[Set[str]] = None, return_data: bool = True, keys_with_alpha_channel: Optional[Set[str]] = None) -> Dict[str, Union[np.ndarray, List[np.ndarray]]]: """ Render all frames. This will go through all frames from scene.frame_start to scene.frame_end and render each of them. :param output_dir: The directory to write files to, if this is None the temporary directory is used. \ The temporary directory is usually in the shared memory (only true for linux). :param file_prefix: The prefix to use for writing the images. :param output_key: The key to use for registering the output. :param load_keys: Set of output keys to load when available :param return_data: Whether to load and return generated data. Backwards compatibility to config-based pipeline. :param keys_with_alpha_channel: A set containing all keys whose alpha channels should be loaded. :return: dict of lists of raw renderer output. Keys can be 'distance', 'colors', 'normals' """ if output_dir is None: output_dir = Utility.get_temporary_directory() if load_keys is None: load_keys = {'colors', 'distance', 'depth', 'normals', 'diffuse'} keys_with_alpha_channel = {'colors'} if bpy.context.scene.render.film_transparent else None if output_key is not None: Utility.add_output_entry({ "key": output_key, "path": os.path.join(output_dir, file_prefix) + "%04d" + map_file_format_to_file_ending(bpy.context.scene.render.image_settings.file_format), "version": "2.0.0" }) load_keys.add(output_key) bpy.context.scene.render.filepath = os.path.join(output_dir, file_prefix) # Skip if there is nothing to render if bpy.context.scene.frame_end != bpy.context.scene.frame_start: if len(get_all_blender_mesh_objects()) == 0: raise Exception("There are no mesh-objects to render, " "please load an object before invoking the renderer.") # As frame_end is pointing to the next free frame, decrease it by one, as # blender will render all frames in [frame_start, frame_ned] bpy.context.scene.frame_end -= 1 bpy.ops.render.render(animation=True, write_still=True) # Revert changes bpy.context.scene.frame_end += 1 return WriterUtility.load_registered_outputs(load_keys, keys_with_alpha_channel) if return_data else {}
def enable_depth_output(output_dir: Optional[str] = None, file_prefix: str = "depth_", output_key: str = "depth"): """ Enables writing depth images. Depth images will be written in the form of .exr files during the next rendering. :param output_dir: The directory to write files to. :param file_prefix: The prefix to use for writing the files. :param output_key: The key to use for registering the depth output. """ if output_dir is None: output_dir = Utility.get_temporary_directory() bpy.context.scene.render.use_compositing = True bpy.context.scene.use_nodes = True tree = bpy.context.scene.node_tree links = tree.links # Use existing render layer render_layer_node = Utility.get_the_one_node_with_type(tree.nodes, 'CompositorNodeRLayers') # Enable z-buffer pass bpy.context.view_layer.use_pass_z = True # Build output node output_file = tree.nodes.new("CompositorNodeOutputFile") output_file.base_path = output_dir output_file.format.file_format = "OPEN_EXR" output_file.file_slots.values()[0].path = file_prefix # Feed the Z-Buffer output of the render layer to the input of the file IO layer links.new(render_layer_node.outputs["Depth"], output_file.inputs['Image']) Utility.add_output_entry({ "key": output_key, "path": os.path.join(output_dir, file_prefix) + "%04d" + ".exr", "version": "2.0.0", "trim_redundant_channels": True })
def enable_normals_output(output_dir: Optional[str] = None, file_prefix: str = "normals_", output_key: str = "normals"): """ Enables writing normal images. Normal images will be written in the form of .exr files during the next rendering. :param output_dir: The directory to write files to, if this is None the temporary directory is used. :param file_prefix: The prefix to use for writing the files. :param output_key: The key to use for registering the normal output. """ if output_dir is None: output_dir = Utility.get_temporary_directory() bpy.context.scene.render.use_compositing = True bpy.context.scene.use_nodes = True tree = bpy.context.scene.node_tree links = tree.links # Use existing render layer render_layer_node = Utility.get_the_one_node_with_type(tree.nodes, 'CompositorNodeRLayers') separate_rgba = tree.nodes.new("CompositorNodeSepRGBA") space_between_nodes_x = 200 space_between_nodes_y = -300 separate_rgba.location.x = space_between_nodes_x separate_rgba.location.y = space_between_nodes_y links.new(render_layer_node.outputs["Normal"], separate_rgba.inputs["Image"]) combine_rgba = tree.nodes.new("CompositorNodeCombRGBA") combine_rgba.location.x = space_between_nodes_x * 14 c_channels = ["R", "G", "B"] offset = space_between_nodes_x * 2 multiplication_values: List[List[bpy.types.Node]] = [[], [], []] channel_results = {} for row_index, channel in enumerate(c_channels): # matrix multiplication mulitpliers = [] for column in range(3): multiply = tree.nodes.new("CompositorNodeMath") multiply.operation = "MULTIPLY" multiply.inputs[1].default_value = 0 # setting at the end for all frames multiply.location.x = column * space_between_nodes_x + offset multiply.location.y = row_index * space_between_nodes_y links.new(separate_rgba.outputs[c_channels[column]], multiply.inputs[0]) mulitpliers.append(multiply) multiplication_values[row_index].append(multiply) first_add = tree.nodes.new("CompositorNodeMath") first_add.operation = "ADD" first_add.location.x = space_between_nodes_x * 5 + offset first_add.location.y = row_index * space_between_nodes_y links.new(mulitpliers[0].outputs["Value"], first_add.inputs[0]) links.new(mulitpliers[1].outputs["Value"], first_add.inputs[1]) second_add = tree.nodes.new("CompositorNodeMath") second_add.operation = "ADD" second_add.location.x = space_between_nodes_x * 6 + offset second_add.location.y = row_index * space_between_nodes_y links.new(first_add.outputs["Value"], second_add.inputs[0]) links.new(mulitpliers[2].outputs["Value"], second_add.inputs[1]) channel_results[channel] = second_add # set the matrix accordingly rot_around_x_axis = mathutils.Matrix.Rotation(math.radians(-90.0), 4, 'X') for frame in range(bpy.context.scene.frame_start, bpy.context.scene.frame_end): used_rotation_matrix = CameraUtility.get_camera_pose(frame) @ rot_around_x_axis for row_index in range(3): for column_index in range(3): current_multiply = multiplication_values[row_index][column_index] current_multiply.inputs[1].default_value = used_rotation_matrix[column_index][row_index] current_multiply.inputs[1].keyframe_insert(data_path='default_value', frame=frame) offset = 8 * space_between_nodes_x for index, channel in enumerate(c_channels): multiply = tree.nodes.new("CompositorNodeMath") multiply.operation = "MULTIPLY" multiply.location.x = space_between_nodes_x * 2 + offset multiply.location.y = index * space_between_nodes_y links.new(channel_results[channel].outputs["Value"], multiply.inputs[0]) if channel == "G": multiply.inputs[1].default_value = -0.5 else: multiply.inputs[1].default_value = 0.5 add = tree.nodes.new("CompositorNodeMath") add.operation = "ADD" add.location.x = space_between_nodes_x * 3 + offset add.location.y = index * space_between_nodes_y links.new(multiply.outputs["Value"], add.inputs[0]) add.inputs[1].default_value = 0.5 output_channel = channel if channel == "G": output_channel = "B" elif channel == "B": output_channel = "G" links.new(add.outputs["Value"], combine_rgba.inputs[output_channel]) output_file = tree.nodes.new("CompositorNodeOutputFile") output_file.base_path = output_dir output_file.format.file_format = "OPEN_EXR" output_file.file_slots.values()[0].path = file_prefix output_file.location.x = space_between_nodes_x * 15 links.new(combine_rgba.outputs["Image"], output_file.inputs["Image"]) Utility.add_output_entry({ "key": output_key, "path": os.path.join(output_dir, file_prefix) + "%04d" + ".exr", "version": "2.0.0" })