Render Comparison Blender Add-on to Spark your Creativity

Sometimes, I want to look at different versions of a setting, but want to remember what setting I used. Therefore, I created a render comparison add-on that renders multiple versions and adds text to the images so you can compare different versions.

To test out this add-on, I used our Alien OSL shader. The full code for this add-on is available in our GitHub repository.

Render Comparison: Alien OSL shader with different levels of roughness

How to Use the Render Comparison Add-on

Open the script and run it. This will add the add-on to the 3D Viewport n-panel. You can access it by pressing n and selecting the Misc tab.

  • numRenders: how many renders you want, this is limited by the number of the render slots.
  • targetName: name of the property to print on the screen
  • target: full datapath to the target
  • start: starting value of the property (float or int)
  • end: ending value of the property (float or int)

To get the full path of the property you want to change, right click on the property you want, select copy full data path. Paste this in the target box.

Set the starting and ending values and press the “Render Instance Settings” button. Keep in mind that rendering will take some time. There is a progress indicator, but it is easy to miss.

The Render Comparison Script

Below, we explain the following parts of the script:

Add-on Basics

For the basics of creating an add-on panel, see this post. This explains the:

  • bl_info dictionary
  • RenderSettingsPanel class
  • register() function
  • unregister() function

Property Group

The UI panel collects five different fields. We will store these in properties attached to the scene. We set up these properties in the RenderSettingsPropertyGroup class. For additional information on how Blender properties work, see this post.

# Property Group for the settings
class RenderSettingsPropertyGroup(bpy.types.PropertyGroup):
    numRenders: bpy.props.IntProperty(name="numRenders",default=1)
    targetName: bpy.props.StringProperty(name="targetName",description="Name of the setting that will change.")
    target: bpy.props.StringProperty(name="target", description="datapath of setting we want to change")
    start: bpy.props.FloatProperty(name="start", default=0.0, description="starting value")
    end: bpy.props.FloatProperty(name="end", default=1.0, description="ending value")

Create Text Object

Blender doesn’t provide an easy way to add text to a render. I wish this was an option in the compositor. Maybe a future project. For now, we need to add a text object to the scene. We use the object add operator. We parent it to the camera, set the location and scale. Then we call CreateMaterial, sending the text object as a parameter.

# Create the text
bpy.ops.object.add(type="FONT")
currText = bpy.context.object
currText.parent = bpy.data.objects["Camera"]
currText.location = (-1.4,-0.696,-4.0)
currText.scale = (0.3,0.3,0.3)
self.CreateMaterial(currText)

Create Material

We need to add a material to the text. In particular, we want the text to appear regardless of where the light in the scene is. Therefore, we need to add an emission shader. However, we don’t want the shader to affect the scene, so we set the emission low (0.3).

We start by checking whether we already have the texture. If not, we create the material. We turn on the nodes. The default setup uses Principled BSDF. We set the emission input color to white and the strength to 0.3.

Finally, we add the material to the text object.

    def CreateMaterial(self,text_object):
        mat = bpy.data.materials.get("TextMaterial")
        if mat is None:
            mat = bpy.data.materials.new(name="TextMaterial")
        
        if not mat.use_nodes:
            mat.use_nodes = True
            nodes = mat.node_tree.nodes

        # Add emission
        bsdf = mat.node_tree.nodes["Principled BSDF"]
        bsdf.inputs['Emission'].default_value = (1.0,1.0,1.0,1.0)
        bsdf.inputs['Emission Strength'].default_value = 0.3
        
        
        
        if text_object.data.materials:
            text_object.materials[0] = mat
        else:
            text_object.data.materials.append(mat)

The Render Comparison Loop

Next, we set up the basic loop. We start by creating our iteration variables. To create the variables, we need to know whether the Blender property is a float or int.

        # Determine the type of the target
        
        isInt = eval('type(' + context.scene.render_settings_prop.target + ')== int')
        
        # Set the render seetings
        startingValue = context.scene.render_settings_prop.start
        iterations = int(min(context.scene.render_settings_prop.numRenders,len(renderSlots)))
        incrementalValue = (context.scene.render_settings_prop.end - startingValue)/max(1,iterations-1)
        
        if isInt:
            startingValue = int(startingValue)
            incrementalValue = int(incrementalValue)

Note that the Python eval() function is not safe. A malicious user could inject anything into the function. However, this was the simplest way to use the path. A safer, alternative method would be to present the user with drop downs (similar to the drop downs in the driver panel).

Next, we set up the progress indicator. I am not a big fan of the progress indicator. It changes the cursor to show what iterations are complete. To me, it is not intuitive to the user. In addition, it is inconsistent. Sometimes it shows and sometimes it shows the OS spinning indicator. In the future, I would like to change this to a progress bar.

        wm = bpy.context.window_manager
        totalProgress = iterations
        wm.progress_begin(0,totalProgress)

Finally, we execute the loop. Each iteration, we:

  • Update the render slot
  • Change the property value
  • Update the text
  • Render the image
  • Update the progress indicator
        for index in range(0,iterations):
            renderSlots.active_index = index
            
            # Change the setting - using exec is not safe
            currentValue = startingValue + incrementalValue*index
            
            exec(context.scene.render_settings_prop.target + "= " + str(currentValue))
            currText.data.body = bpy.context.scene.render_settings_prop.targetName + ": {:.2f}".format(currentValue)
            bpy.ops.render.render()
            wm.progress_update(index)

Clean Up

Finally, we clean up everything. We delete the text and end the progress indicator.

        # Delete the text
        bpy.ops.object.select_all(action="DESELECT")
        currText.select_set(True)
        bpy.context.view_layer.objects.active = currText
        bpy.ops.object.delete()
        wm.progress_end()

Conclusion

This script allows a user to render multiple versions with a changing setting. This is useful for finding just the right setting. There is definitely more we can do to improve this script. In future iterations, we plan to add more settings that we change (e.g., color), potentially allow us to change multiple settings at once, and add a better progress indicator.