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 write_attributes_to_file(self, item_writer, items, default_file_prefix, default_output_key, default_attributes, version="1.0.0"): """ Writes the state of the given items to a file with the configured prefix. This method also registers the corresponding output. :param item_writer: The item writer object to use. Type: object. :param items: The list of items. Type: list. :param default_file_prefix: The default file name prefix to use. Type: string. :param default_output_key: The default output key to use. Type: string. :param default_attributes: The default attributes to write, if no attributes are specified in the config. Type: list. :param version: The version to use when registering the output. Type: string. """ if self._avoid_output: print("Avoid output is on, no output produced!") return file_prefix = self.config.get_string("output_file_prefix", default_file_prefix) path_prefix = os.path.join(self._determine_output_dir(), file_prefix) item_writer.write_items_to_file( path_prefix, items, self.config.get_list("attributes_to_write", default_attributes), world_frame_change=self.destination_frame) Utility.register_output( self._determine_output_dir(), file_prefix, self.config.get_string("output_key", default_output_key), ".npy", version)
def set_type(self, type: str, frame: int = None): """ Sets the type of the light. :param type: The type to set, can be one of [POINT, SUN, SPOT, AREA]. :param frame: The frame number which the value should be set to. If None is given, the current frame number is used. """ self.blender_obj.data.type = type Utility.insert_keyframe(self.blender_obj.data, "type", frame)
def set_distance(self, distance: float, frame: int = None): """ Sets the falloff distance of the light = point where light is half the original intensity. :param distance: The falloff distance to set. :param frame: The frame number which the value should be set to. If None is given, the current frame number is used. """ self.blender_obj.data.distance = distance Utility.insert_keyframe(self.blender_obj.data, "distance", frame)
def set_color(self, color: Union[list, Color], frame: int = None): """ Sets the color of the light. :param color: The rgb color to set. :param frame: The frame number which the value should be set to. If None is given, the current frame number is used. """ self.blender_obj.data.color = color Utility.insert_keyframe(self.blender_obj.data, "color", frame)
def set_energy(self, energy: float, frame: int = None): """ Sets the energy of the light. :param energy: The energy to set. If the type is SUN this value is interpreted as Watt per square meter, otherwise it is interpreted as Watt. :param frame: The frame number which the value should be set to. If None is given, the current frame number is used. """ self.blender_obj.data.energy = energy Utility.insert_keyframe(self.blender_obj.data, "energy", frame)
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 run(self): """ Does the stereo global matching in the following steps: 1. Collect camera object and its state, 2. For each frame, load left and right images and call the `sgm()` methode. 3. Write the results to a numpy file. """ if self._avoid_output: print("Avoid output is on, no output produced!") return if GlobalStorage.is_in_storage("renderer_distance_end"): depth_max = GlobalStorage.get("renderer_distance_end") else: raise RuntimeError( "A distance rendering has to be executed before this module is executed, " "else the `renderer_distance_end` is not set!") rgb_output_path = Utility.find_registered_output_by_key( self.rgb_output_key)["path"] color_images = [] for frame in range(bpy.context.scene.frame_start, bpy.context.scene.frame_end): path_split = rgb_output_path.split(".") path_l = "{}_L.{}".format(path_split[0], path_split[1]) path_r = "{}_R.{}".format(path_split[0], path_split[1]) imgL = load_image(path_l % frame) imgR = load_image(path_r % frame) color_images.append(np.stack((imgL, imgR), 0)) depth, disparity = stereo_global_matching( color_images=color_images, depth_max=depth_max, window_size=self.config.get_int("window_size", 7), num_disparities=self.config.get_int("num_disparities", 32), min_disparity=self.config.get_int("min_disparity", 0), disparity_filter=self.config.get_bool("disparity_filter", True), depth_completion=self.config.get_bool("depth_completion", True)) for frame in range(bpy.context.scene.frame_start, bpy.context.scene.frame_end): np.save( os.path.join(self._determine_output_dir(), "stereo-depth_%04d") % frame, depth[frame]) if self.config.get_bool("output_disparity", False): np.save( os.path.join(self._determine_output_dir(), "disparity_%04d") % frame, disparity[frame]) Utility.register_output(self._determine_output_dir(), "stereo-depth_", "stereo-depth", ".npy", "1.0.0") if self.config.get_bool("output_disparity", False): Utility.register_output(self._determine_output_dir(), "disparity_", "disparity", ".npy", "1.0.0")
def set_scale(self, scale: Union[list, np.ndarray, Vector], frame: int = None): """ Sets the scale of the entity along all three axes. :param scale: The scale to set. :param frame: The frame number which the value should be set to. If None is given, the current frame number is used. """ self.blender_obj.scale = scale Utility.insert_keyframe(self.blender_obj, "scale", frame)
def set_rotation_euler(self, rotation_euler: Union[list, Euler, np.ndarray], frame: int = None): """ Sets the rotation of the entity in euler angles. :param rotation_euler: The euler angles to set. :param frame: The frame number which the value should be set to. If None is given, the current frame number is used. """ self.blender_obj.rotation_euler = rotation_euler Utility.insert_keyframe(self.blender_obj, "rotation_euler", frame)
def set_location(self, location: Union[list, Vector, np.ndarray], frame: int = None): """ Sets the location of the entity in 3D world coordinates. :param location: The location to set. :param frame: The frame number which the value should be set to. If None is given, the current frame number is used. """ self.blender_obj.location = location Utility.insert_keyframe(self.blender_obj, "location", frame)
def __init__(self, config): Module.__init__(self, config) object_pose_sampler_config = config.get_raw_dict( "object_pose_sampler", {}) camera_pose_sampler_config = config.get_raw_dict( "camera_pose_sampler", {}) self._object_pose_sampler = Utility.initialize_modules( [object_pose_sampler_config])[0] self._camera_pose_sampler = Utility.initialize_modules( [camera_pose_sampler_config])[0]
def run(self): """ Hides objects for the set number of frames. """ objects = self.config.get_list("selector") number_of_frames = self.config.get_int("number_of_frames", bpy.context.scene.frame_end) if number_of_frames > bpy.context.scene.frame_end: number_of_frames = bpy.context.scene.frame_end print( f"Will use {number_of_frames} number of frames, for {len(objects)} objects." ) # iterate over all objects for obj in objects: obj.hide_render = False # Insert all selected objects to each frame for normal rendering. for frame in range(number_of_frames): Utility.insert_keyframe(obj, "hide_render", frame) # Insert updated selected objects to each frame with the field hide_render modified obj.hide_render = True for frame in range(number_of_frames): Utility.insert_keyframe(obj, "hide_render", frame + bpy.context.scene.frame_end) # Copy and modify camera location and rotation to the new frames where objects are hidden. camera = bpy.context.scene.camera for frame in range(number_of_frames): bpy.context.scene.frame_set(frame) Utility.insert_keyframe(camera, "location", frame + bpy.context.scene.frame_end) Utility.insert_keyframe(camera, "rotation_euler", frame + bpy.context.scene.frame_end) bpy.context.scene.frame_end += number_of_frames
def set_cp(self, key: str, value: Any, frame: int = None): """ Sets the custom property with the given key. Keyframes can be only set for custom properties for the types int, float or bool. :param key: The key of the custom property. :param value: The value to set. :param frame: The frame number which the value should be set to. If None is given, the current frame number is used. """ self.blender_obj[key] = value if isinstance(self.blender_obj[key], float) or isinstance( self.blender_obj[key], int): Utility.insert_keyframe(self.blender_obj, "[\"" + key + "\"]", frame)
def _colorize_object(obj: bpy.types.Object, color: mathutils.Vector, use_alpha_channel: bool): """ Adjusts the materials of the given object, s.t. they are ready for rendering the seg map. This is done by replacing all nodes just with an emission node, which emits the color corresponding to the category of the object. :param obj: The object to use. :param color: RGB array of a color in the range of [0, self.render_colorspace_size_per_dimension]. :param use_alpha_channel: If true, the alpha channel stored in .png textures is used. """ # Create new material emitting the given color new_mat = bpy.data.materials.new(name="segmentation") new_mat.use_nodes = True nodes = new_mat.node_tree.nodes links = new_mat.node_tree.links emission_node = nodes.new(type='ShaderNodeEmission') output = Utility.get_the_one_node_with_type(nodes, 'OutputMaterial') emission_node.inputs['Color'].default_value[:3] = color links.new(emission_node.outputs['Emission'], output.inputs['Surface']) # Set material to be used for coloring all faces of the given object if len(obj.material_slots) > 0: for i in range(len(obj.material_slots)): if use_alpha_channel: obj.data.materials[ i] = MaterialLoaderUtility.add_alpha_texture_node( obj.material_slots[i].material, new_mat) else: obj.data.materials[i] = new_mat else: obj.data.materials.append(new_mat)
def light_suncg_scene(lightbulb_emission_strength: float = 15, lampshade_emission_strength: float = 7, ceiling_emission_strength: float = 1.5): """ Makes the lamps, windows and ceilings object emit light. :param lightbulb_emission_strength: The emission strength that should be used for light bulbs. Default: 15 :param lampshade_emission_strength: The emission strength that should be used for lamp shades. Default: 7 :param ceiling_emission_strength: The emission strength that should be used for the ceiling. Default: 1.5 """ # Read in the materials for lights and windows lights, windows = Utility.read_suncg_lights_windows_materials() collection_of_mats: Dict[str, Dict[str, Material]] = {"lamp": {}, "window": {}, "ceiling": {}} # Make some objects emit lights for obj in get_all_mesh_objects(): if obj.has_cp("modelId"): obj_id = obj.get_cp("modelId") # In the case of the lamp if obj_id in lights: SuncgLighting._make_lamp_emissive(obj, lights[obj_id], collection_of_mats, lightbulb_emission_strength, lampshade_emission_strength) # Make the windows emit light if obj_id in windows: SuncgLighting._make_window_emissive(obj, collection_of_mats) # Also make ceilings slightly emit light if obj.get_name().startswith("Ceiling#"): SuncgLighting._make_ceiling_emissive(obj, collection_of_mats, ceiling_emission_strength)
def build_convex_decomposition_collision_shape( self, vhacd_path: str, temp_dir: str = None, cache_dir: str = "blenderproc_resources/decomposition_cache"): """ Builds a collision shape of the object by decomposing it into near convex parts using V-HACD :param vhacd_path: The directory in which vhacd should be installed or is already installed. :param temp_dir: The temp dir to use for storing the object files created by v-hacd. :param cache_dir: If a directory is given, convex decompositions are stored there named after the meshes hash. If the same mesh is decomposed a second time, the result is loaded from the cache and the actual decomposition is skipped. """ if platform == "win32": raise Exception("This is currently not supported under Windows") if temp_dir is None: temp_dir = Utility.get_temporary_directory() # Decompose the object parts = convex_decomposition(self, temp_dir, resolve_path(vhacd_path), cache_dir=resolve_path(cache_dir)) parts = [MeshObject(p) for p in parts] # Make the convex parts children of this object, enable their rigid body component and hide them for part in parts: part.set_parent(self) part.enable_rigidbody(True, "CONVEX_HULL") part.hide()
def _colorize_objects_for_instance_segmentation(objects: List[bpy.types.Object], use_alpha_channel: bool, render_colorspace_size_per_dimension: int) \ -> Tuple[List[List[int]], int, List[bpy.types.Object]]: """ Sets a different color to each object. :param objects: A list of objects. :param use_alpha_channel: If true, the alpha channel stored in .png textures is used. :param render_colorspace_size_per_dimension: The limit of the colorspace to use per dimension for generating colors. :return: The num_splits_per_dimension of the spanned color space, the color map """ # + 1 for the background colors, num_splits_per_dimension = Utility.generate_equidistant_values( len(objects) + 1, render_colorspace_size_per_dimension) # this list maps ids in the image back to the objects color_map = [] # Set world background label, which is always label zero _set_world_background_color(colors[0]) color_map.append(bpy.context.scene.world ) # add the world background as an object to this list for idx, obj in enumerate(objects): _colorize_object(obj, colors[idx + 1], use_alpha_channel) color_map.append(obj) return colors, num_splits_per_dimension, color_map
def _disable_all_denoiser(): """ Disables all denoiser. At the moment this includes the cycles and the intel denoiser. """ # Disable cycles denoiser bpy.context.view_layer.cycles.use_denoising = False # Disable intel denoiser if bpy.context.scene.use_nodes: nodes = bpy.context.scene.node_tree.nodes links = bpy.context.scene.node_tree.links # Go through all existing denoiser nodes for denoiser_node in Utility.get_nodes_with_type(nodes, 'CompositorNodeDenoise'): in_node = denoiser_node.inputs['Image'] out_node = denoiser_node.outputs['Image'] # If it is fully included into the node tree if in_node.is_linked and out_node.is_linked: # There is always only one input link in_link = in_node.links[0] # Connect from_socket of the incoming link with all to_sockets of the out going links for link in out_node.links: links.new(in_link.from_socket, link.to_socket) # Finally remove the denoiser node nodes.remove(denoiser_node)
def get_nodes_with_type(self, node_type: str) -> List[bpy.types.Node]: """ Returns all nodes which are of the given node_type :param node_type: The note type to look for. :return: The list of nodes with the given type. """ return Utility.get_nodes_with_type(self.nodes, node_type)
def set_world_background_hdr_img(path_to_hdr_file): """ Sets the world background to the given hdr_file. :param path_to_hdr_file: Path to the .hdr file """ if not os.path.exists(path_to_hdr_file): raise Exception( "The given path does not exists: {}".format(path_to_hdr_file)) world = bpy.context.scene.world nodes = world.node_tree.nodes links = world.node_tree.links # add a texture node and load the image and link it texture_node = nodes.new(type="ShaderNodeTexEnvironment") texture_node.image = bpy.data.images.load(path_to_hdr_file, check_existing=True) # get the one output node of the world shader output_node = Utility.get_the_one_node_with_type(nodes, "Output") # link the new texture node to the output links.new(texture_node.outputs["Color"], output_node.inputs["Surface"])
def _load_texture(cur_obj: bpy.types.Object, texture_file_path: str, bop_dataset_name: str): """ Load the textures for the ycbv objects, only those contain texture information :param cur_obj: The object to use. :param texture_file_path: path to the texture file (most likely ".png") :param bop_dataset_name: The name of the used bop dataset. """ mat = bpy.data.materials.new(name="bop_" + bop_dataset_name + "_texture_material") mat.use_nodes = True nodes = mat.node_tree.nodes links = mat.node_tree.links color_image = nodes.new('ShaderNodeTexImage') if not os.path.exists(texture_file_path): raise Exception("The texture path for the ycbv object could not be loaded from the " "file: {}".format(texture_file_path)) color_image.image = bpy.data.images.load(texture_file_path, check_existing=True) principled = Utility.get_the_one_node_with_type(nodes, "BsdfPrincipled") links.new(color_image.outputs["Color"], principled.inputs["Base Color"]) if cur_obj.data.materials: # assign to 1st material slot cur_obj.data.materials[0] = mat else: # no slots cur_obj.data.materials.append(mat)
def add_nodes_to_group(nodes: bpy.types.Node, group_name: str) -> bpy.types.ShaderNodeTree: """ Creates the node group, copies all attributes and links and adds the group input and output https://blender.stackexchange.com/a/175604 :param nodes: Nodes, which should be used :param group_name: Name of the group :return bpy.types.ShaderNodeTree: the group which can be used inside of a bpy.types.ShaderNodeGroup """ # create new node group group = bpy.data.node_groups.new(name=group_name, type="ShaderNodeTree") # copy all nodes from the list to the created group with all their attributes copy_nodes(nodes, group.nodes) # copy the links between the nodes to the created groups nodes copy_links(nodes, group.nodes, group.links) # add the group input and output node to the created group group_input, group_output = add_group_nodes(group) # check if the selection of nodes goes over a material, if so replace the material output with the output of # the group material_outputs = Utility.get_nodes_with_type(group.nodes, "OutputMaterial") if len(material_outputs) == 1: for input in material_outputs[0].inputs: group.outputs.new(input.bl_idname, input.name) for link in input.links: group.links.new(link.from_socket, group_output.inputs[input.name]) # remove the material output, the material output should never be inside of a group group.nodes.remove(material_outputs[0]) return group
def run(self): # if the rendering is not performed -> it is probably the debug case. do_undo = not self._avoid_output with Utility.UndoAfterExecution(perform_undo_op=do_undo): self._configure_renderer(use_denoiser=True, default_denoiser="Intel") # check if texture less render mode is active if self._texture_less_mode: MaterialLoaderUtility.change_to_texture_less_render( self._use_alpha_channel) if self._use_alpha_channel: MaterialLoaderUtility.add_alpha_channel_to_textures( blurry_edges=True) # motion blur if self._use_motion_blur: RendererUtility.enable_motion_blur( self._motion_blur_length, 'TOP' if self._use_rolling_shutter else "NONE", self._rolling_shutter_length) self._render("rgb_", "colors", enable_transparency=self.config.get_bool( "transparent_background", False), file_format=self._image_type)
def _adjust_material_nodes(mat: Material, adjustments: Dict[str, str]): """ Adjust the material node of the given material according to the given adjustments. Textures or diffuse colors will be changed according to the given material_adjustments. :param mat: The blender material. :param adjustments: A dict containing a new "diffuse" color or a new "texture" path """ if "diffuse" in adjustments: principle_node = mat.get_the_one_node_with_type("BsdfPrincipled") principle_node.inputs[ 'Base Color'].default_value = Utility.hex_to_rgba( adjustments["diffuse"]) if "texture" in adjustments: image_path = os.path.join(SuncgLoader._suncg_dir, "texture", adjustments["texture"]) image_path = resolve_path(image_path) if os.path.exists(image_path + ".png"): image_path += ".png" else: image_path += ".jpg" image_node = mat.get_the_one_node_with_type("ShaderNodeTexImage") if os.path.exists(image_path): image_node.image = bpy.data.images.load(image_path, check_existing=True) else: print( "Warning: Cannot load texture, path does not exist: {}, remove image node again" .format(image_path)) mat.remove_node(image_node)
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 get_the_one_node_with_type(self, node_type: str) -> bpy.types.Node: """ Returns the one node which is of the given node_type This function will only work if there is only one of the nodes of this type. :param node_type: The node type to look for. :return: The node. """ return Utility.get_the_one_node_with_type(self.nodes, node_type)
def insert_node_instead_existing_link( self, source_socket: bpy.types.NodeSocket, new_node_dest_socket: bpy.types.NodeSocket, new_node_src_socket: bpy.types.NodeSocket, dest_socket: bpy.types.NodeSocket): """ Replaces the node between source_socket and dest_socket with a new node. Before: source_socket -> dest_socket After: source_socket -> new_node_dest_socket and new_node_src_socket -> dest_socket :param source_socket: The source socket. :param new_node_dest_socket: The new destination for the link starting from source_socket. :param new_node_src_socket: The new source for the link towards dest_socket. :param dest_socket: The destination socket """ Utility.insert_node_instead_existing_link(self.links, source_socket, new_node_dest_socket, new_node_src_socket, dest_socket)
def _default_init(self): """ These operations are called during all modules inits """ self._output_dir = resolve_path( self.config.get_string("output_dir", "")) os.makedirs(self._output_dir, exist_ok=True) self._temp_dir = Utility.get_temporary_directory() self._avoid_output = self.config.get_bool("avoid_output", False)
def set_denoiser(denoiser: Optional[str]): """ Enables the specified denoiser. Automatically disables all previously activated denoiser. :param denoiser: The name of the denoiser which should be enabled. Options are "INTEL", "BLENDER" and None. \ If None is given, then no denoiser will be active. """ # Make sure there is no denoiser active _disable_all_denoiser() if denoiser is None: pass elif denoiser.upper() == "INTEL": # The intel denoiser is activated via the compositor bpy.context.scene.use_nodes = True nodes = bpy.context.scene.node_tree.nodes links = bpy.context.scene.node_tree.links # The denoiser gets normal and diffuse color as input bpy.context.view_layer.use_pass_normal = True bpy.context.view_layer.use_pass_diffuse_color = True # Add denoiser node denoise_node = nodes.new("CompositorNodeDenoise") # Link nodes render_layer_node = Utility.get_the_one_node_with_type(nodes, 'CompositorNodeRLayers') composite_node = Utility.get_the_one_node_with_type(nodes, 'CompositorNodeComposite') Utility.insert_node_instead_existing_link(links, render_layer_node.outputs['Image'], denoise_node.inputs['Image'], denoise_node.outputs['Image'], composite_node.inputs['Image']) links.new(render_layer_node.outputs['DiffCol'], denoise_node.inputs['Albedo']) links.new(render_layer_node.outputs['Normal'], denoise_node.inputs['Normal']) elif denoiser.upper() == "BLENDER": bpy.context.view_layer.cycles.use_denoising = True else: raise Exception("No such denoiser: " + denoiser)