2
$\begingroup$

I have another issue maybe related to my previous thread but I think they are different as the previous one was related to baking multiple times with only slight subtle differences. This issue however is just about baking a certain UV map and color once and results are already significantly different. I have this object and these bake settings:

s = bpy.context.scene
s.cycles.device = 'GPU'
s.render.engine = 'CYCLES'
s.cycles.use_adaptive_sampling = False
s.cycles.samples = 1
s.cycles.bake_type = 'DIFFUSE'
s.render.bake.use_pass_direct = False
s.render.bake.use_pass_indirect = False
s.render.bake.margin = 10
s.cycles.use_denoising = False

After baking notice how darker it has become:

enter image description here

Here is the blend file with a script so you don't have to manually bake it. You can also try manually baking it without the script, it will yield the same result.

Blender 4.1

$\endgroup$

1 Answer 1

1
+100
$\begingroup$

The canonical answer

There is no canonical solution for a robust all-encompassing baking in Blender.

Blender shaders cannot be distilled to a set of PBR maps without a lot of assumptions. The baking itself didn't get enough attention from the devs and suffers from fundamental issues and requires workarounds.

It is an extensive topic and would take hundreds of hours and thousands lines of code.

Your code

If it is not to cover some niche application and will not grow much further is flawed. It needs a refactor. It will break for many more reasons. It should not be a singleton. You should not code in the Text Editor. You should develop an API wrapper for the native nodes API. You should create a test suit for testing against many different blend files right a way.

The question

Subsequent darkening

Diffuse color is not the same as base color, so you can't bake it as diffuse and expect to get the base color.

https://projects.blender.org/blender/blender/issues/69826#issuecomment-397246

To get the exact Base Color input color you need to bake it using the Emit bake type.

A modified function to bake the Base Color using an Emission shader which you have to replace the bake function from your blend file with. This prevents the color from darkening each bake.


    def bake(self, obj):
        self.bake_images = []
        self.add_image_texture_for_baking(obj)
        self.prev_uvmap_names = [uv_layer.name for uv_layer in obj.data.uv_layers]
        self.create_uv_map_and_unwrap(obj)
        
        
        nodes_to_delete = []
        links_to_reconnect = []

        for material_slot in obj.material_slots:

            material = material_slot.material
            if not material or not material.node_tree:
                continue

            for target in ('ALL', 'CYCLES', 'EEVEE'):
                active_output = material.node_tree.get_output_node(target)
                if active_output:
                    break
            else:
                continue
            
            active_output_link = active_output.inputs[0].links[0]
            
            from_node = active_output_link.from_node
            # assuming it is ShaderNodeBsdfPrincipled and the first input is Base Color, also works with other shaders
            
            if from_node.inputs[0].links:
                shader_node_base_color_link = from_node.inputs[0].links[0]
                base_color_output_socket = shader_node_base_color_link.from_socket
            else:
                _input_socket = from_node.inputs[0]
                nodes = material.node_tree.nodes
                
                if _input_socket.type in ('VALUE', 'INT'):
                    _node = nodes.new('ShaderNodeValue')
                    _node.outputs[0].default_value = _input_socket.default_value
                    nodes_to_delete.append(_node)
                    base_color_output_socket = _node.outputs[0]

                elif _input_socket.type == 'RGBA':
                    _node = nodes.new('ShaderNodeRGB')
                    _node.outputs[0].default_value = _input_socket.default_value
                    nodes_to_delete.append(_node)
                    base_color_output_socket = _node.outputs[0]

                elif _input_socket.type == 'VECTOR':
                    _node = nodes.new('ShaderNodeCombineXYZ')
                    _node.inputs['X'].default_value, _node.inputs['Y'].default_value, _node.inputs['Z'].default_value = tuple(_input_socket.__data__.default_value)
                    nodes_to_delete.append(_node)
                    base_color_output_socket = _node.outputs[0]
            

            emission_node = material.node_tree.nodes.new(type='ShaderNodeEmission')
            
            nodes_to_delete.append(emission_node)
            links_to_reconnect.append((active_output_link.from_socket, active_output_link.to_socket))
            
            material.node_tree.links.new(emission_node.outputs[0], active_output.inputs[0])
            material.node_tree.links.new(base_color_output_socket, emission_node.inputs[0])
        
        bpy.ops.object.bake(type='EMIT')
        
        for node in nodes_to_delete:
            nodes = node.id_data.nodes
            nodes.remove(node)
            
        for link in links_to_reconnect:
            links = link[0].id_data.links
            links.new(link[0], link[1])

        for uvname in self.prev_uvmap_names:
            obj.data.uv_layers.remove(obj.data.uv_layers[uvname])
        
        self.setup_new_material_with_baked_texture(obj)

    def add_image_texture_for_baking(self, obj):
        D = self.data
        self.bake_image = D.images.new(f"Image.{obj.name}", 200, 200)
        print(f"Created temporary bake image: {self.bake_image}")
        
        for material_slot in obj.material_slots:

            material = material_slot.material
            if not material or not material.node_tree:
                continue
            
            mat = material
            nodes = mat.node_tree.nodes
            for n in nodes:
                n.select = False
            tex_node = nodes.new(type='ShaderNodeTexImage')
            tex_node.image = self.bake_image
            tex_node.name = mat.name
            tex_node.select = True
            nodes.active = tex_node

Initial darkening

The first significant darkening happens due to how Cycles and EEVEE handle texture filtering. Your object's UVs are very small compared to the size of the pixels of the image used. Each UV island samples a very small part of a pixel so the difference in the texture filtering between Cycles and EEVEE becomes very apparent. You bake with Cycles so it the Cycles' texture filtering that is getting baked into the texture. If you switch the viewport renderer to Cycles you will see that the colors are the same.

enter image description here

$\endgroup$
5
  • $\begingroup$ I tried the normal default cube with white base color. it turns black final bake result. can you provide a blend file with your test also on a white cube? $\endgroup$
    – Megan Love
    Apr 7 at 15:20
  • $\begingroup$ The script I have provided needs another socket input for the Base Color. It will use the from_socket as an input for the Emission node. If there is no socket input a substitution node with the same output value should be created. You can also convert the input socket default_value to the Color input's default_value value of the Emission node but it is not possible for some combinations if you need to bake outside of the 0-1 range. Like you can bake a negative value but you cannot assign a negative value to a color type socket. $\endgroup$
    – unwave
    Apr 7 at 18:30
  • $\begingroup$ im sorry im not sure I understand i thought i could directly use your code (which bakes black) so i can't automate it? or can you provide a blend file that works for you? because the code doesn't do it automatically apparently. $\endgroup$
    – Megan Love
    Apr 8 at 2:40
  • $\begingroup$ @MeganLove I have updated the code to work for the default cube. $\endgroup$
    – unwave
    Apr 13 at 22:35
  • $\begingroup$ thank you for your unwavering support :) $\endgroup$
    – Megan Love
    Apr 14 at 1:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .