Ejemplo n.º 1
0
    def enable_diffuse_color_output(output_dir: str, 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.
        :param file_prefix: The prefix to use for writing the files.
        :param output_key: The key to use for registering the diffuse color output.
        """
        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"
        })
Ejemplo n.º 2
0
    def enable_depth_output(output_dir, file_prefix="depth_", output_key="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.
        """
        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"
        })
Ejemplo n.º 3
0
    def enable_distance_output(output_dir: Union[str, None] = 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
        })
Ejemplo n.º 4
0
    def render(output_dir: Union[str, None] = None, file_prefix: str = "rgb_", output_key: str = "colors",
               load_keys: Set = None, return_data: bool = True) -> Dict[str, 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.
        :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', 'normals'}

        if output_key is not None:
            Utility.add_output_entry({
                "key": output_key,
                "path": os.path.join(output_dir, file_prefix) + "%04d" +
                        RendererUtility.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) if return_data else {}
Ejemplo n.º 5
0
    def render(output_dir, file_prefix="rgb_", output_key="colors"):
        """ 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 images to.
        :param file_prefix: The prefix to use for writing the images.
        :param output_key: The key to use for registering the output.
        """
        if output_key is not None:
            Utility.add_output_entry({
                "key":
                output_key,
                "path":
                os.path.join(output_dir, file_prefix) + "%04d" +
                RendererUtility.map_file_format_to_file_ending(
                    bpy.context.scene.render.image_settings.file_format),
                "version":
                "2.0.0"
            })

        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_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
Ejemplo n.º 6
0
    def enable_normals_output(output_dir: Union[str, None] = 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 = [[], [], []]
        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')
        cam_ob = bpy.context.scene.camera
        for frame in range(bpy.context.scene.frame_start, bpy.context.scene.frame_end):
            bpy.context.scene.frame_set(frame)
            used_rotation_matrix = cam_ob.matrix_world @ 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"
        })
Ejemplo n.º 7
0
    def enable_distance_output(output_dir,
                               file_prefix="distance_",
                               output_key="distance",
                               use_mist_as_distance=True,
                               distance_start=0.1,
                               distance_range=25.0,
                               distance_falloff="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.
        :param file_prefix: The prefix to use for writing the files.
        :param output_key: The key to use for registering the distance output.
        :param use_mist_as_distance: If true, the distance is sampled over several iterations, useful for motion blur or soft edges, if this is turned off, only one sample is taken to determine the depth. Default: True.
        :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]
        """
        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')

        # use either mist rendering or the z-buffer
        # mists uses an interpolation during the sample per pixel
        # while the z buffer only returns the closest object per pixel
        if use_mist_as_distance:
            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']
        else:
            bpy.context.view_layer.use_pass_z = True
            # add min and max nodes to perform the clipping to the desired range
            min_node = tree.nodes.new("CompositorNodeMath")
            min_node.operation = "MINIMUM"
            min_node.inputs[1].default_value = distance_start + distance_range
            links.new(render_layer_node.outputs["Depth"], min_node.inputs[0])
            max_node = tree.nodes.new("CompositorNodeMath")
            max_node.operation = "MAXIMUM"
            max_node.inputs[1].default_value = distance_start
            links.new(min_node.outputs["Value"], max_node.inputs[0])
            final_output = max_node.outputs["Value"]

        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"
        })