Skip to content

Update to “Save Renderlayers and Passes to Files” script – now supports subfolders

August 9, 2013

The Save Renderlayers and Passes script, as you might recall, creates a node setup that automates the creation of separate files for each renderpass in each renderlayer.

I’ve been asked both here and in BlenderNation to add a feature supporting the distribution of render pass files into subfolders, a feature which is useful especially for rendered animations (where you have multiple frames of each type).

Since I had my hands full of work, I took my sweet ol’ time with it. But then came Luciano Munoz to the rescue, with an elegant solution.

He did some research and modified my script to add this functionality!

Anyone out there still doubts the open source is awesome?

Go ahead and DOWNLOAD the updated script! Its in the same place on GitHub.

Found a bug? Please report it in my Bug Tracker!

New and improved! :-p

New and improved! :-p

I incorporated Luciano’s changes and updated the script further, in two aspects:

1. The script now reads the list of all renderpass names from the renderlayer object in a generalized manner, where previously I hardcoded all the names. Hardcoding = bad, so this is a useful update that will also make it easier to accommodate future API changes in blender. I’ll explain this trick at the end of the post, so as not to bore to death those not interested in Python.

2. I also added a little checkbox to the add-on panel, so that you can choose whether you want to divide files into subfolders or not.
This feature is very basic at the moment, so if you toggle it after you already created the node setup once (by pressing the button), it will not update the existing nodes. You have to manually delete all the nodes and press the button again to create a new setup that takes the new checkbox value into account.
It will be much nicer if it automatically updates the existing setup when you change the checkbox value, but this is a bit more complicated so I’ll leave it to the next version.

To those of you interested in the programming behind the scenes, here are some tips and insights:

Iterating over the attributes of an object class

The renderlayer class has a huge list of “use_pass_X” attributes, which represent all the possible passes and indicate which of these passes are active and need to be rendered with a boolean (True/False) value:

>>> bpy.context.scene.render.layers['RenderLayer'].use_pass_combined
True

But there’s quite a lot of render passes (27 as you’ll see in a moment). Hardcoding all their names is both bad practice (makes updates and maintenance more difficult), error prone and cumbersome. A much nicer approach would be to somehow list all the render pass attributes. This is where the dir function comes in handy:

>>> [ p for p in dir(bpy.context.scene.render.layers['RenderLayer']) if 'use_pass_' in p ]
['use_pass_ambient_occlusion', 'use_pass_color', 'use_pass_combined', 'use_pass_diffuse', 'use_pass_diffuse_color', 'use_pass_diffuse_direct', 'use_pass_diffuse_indirect', 'use_pass_emit', 'use_pass_environment', 'use_pass_glossy_color', 'use_pass_glossy_direct', 'use_pass_glossy_indirect', 'use_pass_indirect', 'use_pass_material_index', 'use_pass_mist', 'use_pass_normal', 'use_pass_object_index', 'use_pass_reflection', 'use_pass_refraction', 'use_pass_shadow', 'use_pass_specular', 'use_pass_transmission_color', 'use_pass_transmission_direct', 'use_pass_transmission_indirect', 'use_pass_uv', 'use_pass_vector', 'use_pass_z']

>>> len( [ p for p in dir(bpy.context.scene.render.layers['RenderLayer']) if 'use_pass_' in p ] )
27

dir is a function that returns all the properties of an object as a list. So when you run it on the renderlayer object, you get all its attributes, including all the ‘use_pass_X’ attributes.

Then, all that’s left is to filter the passes from all the attributes with the if clause at the end of the comprehension, and you get all the passes without having to worry at all about whether you’re in cycles or BI. Sweet!

And since each pass is a boolean letting us know if that render pass is included, we can filter out the inactive passes by checking which of them has a True value.

The usual way to access an attribute and check its value, is via dot notation:

>>> object.attribute
Value

The dot notation doesn’t really allow us to use the nice little list of strings we acquired using the dir function. Gladly, there’s another way:

>>> renderlayer = bpy.context.scene.render.layers['RenderLayer']
>>> passes = [ a for a in dir(renderlayer) if 'use_pass_' in a ]
>>> [ p for p in passes if getattr( renderlayer, p ) ]
['use_pass_ambient_occlusion', 'use_pass_combined', 'use_pass_glossy_direct', 'use_pass_normal', 'use_pass_object_index', 'use_pass_shadow', 'use_pass_z']

That’s exactly what the getattr function is for. This function fetches the value of an object’s attribute, by specifying that attribute’s name as a string.

>>> getattr( object, 'attribute' )   # Is the same as:
>>> object.attribute

The corresponding function is of course setattr, which allows us set the value of an attribute in a similar manner:

>>> setattr( object, 'attribute', value )   # Is the same as:
>>> object.attribute = value

Name inconsistencies

To save passes into files, we need to connect the pass output sockets on the renderlayer node with file output nodes in the node editor. This is why we wanted to iterate over all the active render passes as specified above. But the list of active render passes we acquired above cannot be directly used for this purpose, since the names of the sockets do not match the names of the render passes:

>>> [ o.name for o in bpy.context.scene.node_tree.nodes['RenderLayer'].outputs ]
['Image', 'Alpha', 'Z', 'Normal', 'UV', 'Speed', 'Color', 'Diffuse', 'Specular', 'Shadow', 'AO', 'Reflect', 'Refract', 'Indirect', 'IndexOB', 'IndexMA', 'Mist', 'Emit', 'Environment', 'Diffuse Direct', 'Diffuse Indirect', 'Diffuse Color', 'Glossy Direct', 'Glossy Indirect', 'Glossy Color', 'Transmission Direct', 'Transmission Indirect', 'Transmission Color']
Comparing the names of renderlayer use_pass attributes and the RenderLayerNode output socket names.

Comparing the names of renderlayer use_pass_X attributes and the RenderLayerNode output socket names.

As you can see in this list, some of the strings match except for capital first letters, some also have underscores instead of spaces, and some don’t match at all (for instance: material_index ==> IndexMA).

For those that don’t match at all, I had no choice but to use a hardcoded dictionary again. But the others were easily converted using some string manipulations:

mypass = "transmission_color"

wl = mypass.split("_") # Split to list of words

# Capitalize first char in each word and rejoin with spaces

output = " ".join([ s[0].capitalize() + s[1:] for s in wl ])

So, the script will be easier to update if the API changes, the code is shorter and is more readable, and uses some nice advanced tricks.
It’s also wonderful to involve other members of the community in the development – which is one of the strongest points of releasing source code.

I’m fairly pleased with the new version, and will be glad to hear if it works well for your purposes. Let me know in comments here or via email.

Advertisements
13 Comments
  1. lovely!, i feel so proud of having been able to contribute just a tiny bit to your script that made my life so much easier =)…

    thank you!!

  2. incredibly useful script for short movies! but i can’t make it work with blender 2.68a, once installed it’s not listed in the addon panel. or am I wrong in some way?

  3. Thank you for the script!

  4. hey man, did you ever get around to make it create just the one file output node?

    cheers!!!

  5. So, new update: I started and almost finished writing the single output functionality,
    and then it turned out that a bug in the python API makes this impossible at the moment!
    https://projects.blender.org/tracker/?func=detail&atid=498&aid=36706&group_id=9

    The bug was fixed very promptly by Lukas from the Blender Foundation, but the update is currently only in the SVN branch and will only be accessible in the next official release of Blender.

    So this update will have to wait… hopefully not much though 🙂

Trackbacks & Pingbacks

  1. Estudio Pintamonos » Blender at Estudio Pintamonos.
  2. Estudio Pintamonos » Blender – my favourite add-ons… so far.
  3. Blender, save each render pass separated | brontosaurusrex.mooo.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: