Change a Driver based on which Scene is being Rendered

I am looking to drive a property in a material node based on the current Scene when rendering. The purpose would be to mix between two different parts of the node tree so that one Scene renders one input of a Mix node, and the other scene renders the other. The objects the material is on exist in one scene, and a group instance of the objects exists in the other scene.

The reason for using different Scenes is that part of the material is regular shaders that need to be GPU rendered, and part are OSL shaders that need to be CPU rendered. So the only difference between the scenes is that one has the device set to GPU and the other CPU.

I know that I could make copies of the objects/materials and so have a different material on the OSL version, but this gets cumbersome as i'll need to re-copy the objects or re-create the whole scene every time I change an object. This is too inefficient while still working on the project. The Group instance means that any changes I make to the models are instantly updated in the other scene, and lets me use the Group to control which models are rendered in the OSL scene at all.

Is this possible?

• Can you provide a simple sample file? Not a fan of using global bpy in driver namespace, as in answer below.. If you set up a scene property and use it as a standard driver variable does it also fail? – batFINGER Feb 15 '19 at 8:29
• @batFINGER I've tried a custom property on a scene to run a driver but that has update problems as well. But the bigger problem is finding a variable that changes during render, and that could be updated mid render as well. I'm not sure one exists. – Ascalon Feb 15 '19 at 9:29
• Can you use methods from prior question rarely use AN. Here is a simple template to set up dud render handlers. blender.stackexchange.com/a/62870/15543 Could see if using handlers will set nodes as desired. – batFINGER Feb 15 '19 at 9:41
• @batFINGER The method for render layers from my old thread doesn't solve this problem. It switches the driver based on the layer I have selected i the UI. So it doesn't trigger when rendering. I'll look into the render handlers. Thanks! – Ascalon Feb 15 '19 at 20:37

render init handler

Hoping the render init handler will do what you desire.

I don't have GPU rendering to test, so all I've done is set the diffuse color of simple material "Material" for different scenes based on there name, and render resolution percentage which both work as expected on the scene being rendered.

import bpy

def render_init(scene):
device = 'CPU'
respc = 100
if scene.name == "Scene":
color = (1, 0, 0, 1)
#device = 'GPU'
else:
color = (0, 1, 0, 1)
respc = 25
bpy.data.materials["Material"].node_tree.nodes['Diffuse BSDF'].inputs['Color'].default_value = color
print("Render", scene.name, color)
# set scene stuff
scene.render.resolution_percentage = respc
#scene.render.cycles.device = device
return None

bpy.app.handlers.render_init.clear()
bpy.app.handlers.render_init.append(render_init)


other methods would be to add a custom property say "GPU" only to a scene that you wish to render on GPU and test for it, rather than checking scene name.

if scene.get("GPU"):
device = 'GPU'

• This is sort of working. I have a scene named Scene and one named Test. And instead of color I am just changing a value to be either 0 or 1. (0 in the If, 1 in the else.) If I hit f12, it renders both Scenes as value 0. But if I render them manually by clicking the render button on their compositor node, then it renders them properly with the switch working. So that's a big help, but why isn't it working with f12 render? And could this be made to work on a render layer basis instead of Scene? If I can switch the driver and device on render layer, I don't need more scenes. – Ascalon Feb 19 '19 at 23:53
• Upon further experimentation, it seems that the problem is that it considers rendering both scenes to be only 1 render. So there's no time for the render handler to update things between them. – Ascalon Feb 20 '19 at 23:32

It is possible to script drivers. Normally if you use a scripted expression as a driver, you can use any function that is defined in any script and added to driver namespace:

import bpy

def f():
if bpy.context.scene.name == 'Scene.001':
return 1
else:
return 0

bpy.app.driver_namespace['f'] = f


'Register' would need to be checked in the Text Editor also the script needs to have .py extension and Auto Run Python Scripts must be enabled for the function to exist every time the file is reloaded.

However apparently this does not work with driving material node properties because Blender's drivers seem to be broken in this area.

It would probably be possible to script an app handler that would take care of it, but at this point I would suggest to simply use a script to switch the mix nodes in all of your materials before rendering. Since this extra step takes only a few seconds it might be easier to have less complexity and you could possibly get away with only one scene as well. You could give the Mix node some specific name in every material like for example "OSL_mix"(in the n panel of the Node Editor). Then you could run this to change the values of the nodes with that name in all your materials at once:

import bpy

for m in bpy.data.materials:
for n in m.node_tree.nodes:
if n.name == "OSL_mix":
n.inputs[0].default_value=1 # or 0


For your convenience you could change the render setting with the script as well by adding something like:

bpy.context.scene.cycles.device = 'CPU' # or 'GPU'
bpy.context.scene.cycles.shading_system = True # that's the OSL
bpy.context.scene.cycles.samples = 1000


Or something like that. You can just change what you want in the render settings and copy the command from the Info panel.

I would just have two scripts like that to switch between CPU-OSL and GPU rendering.

I hope that might be a convenient enough workaround.

• This is working but isn't solving the whole problem because this driver doesn't update regularly, or while rendering. The driver updates the first time I change to a different scene, but doesn't when I change back. To have it update again I have to reload the file. It also doesn't change when rendering. Both scenes render with whatever value was in the driver when I hit the render button. So either it doesn't update during the render at all, or the current scene doesn't change during the render. So this is only doing it based on the current UI state. – Ascalon Feb 15 '19 at 1:02
• Seems to work when I test it: media.giphy.com/media/WygKP5JZ5LDAETRPMV/source.gif Could you maybe edit your question and add more detail about the process you are using for rendering so we can troubleshoot this? – Martynas Žiemys Feb 15 '19 at 9:45
• Drivers in Blender... I sometimes wonder what's the point - they seem not to work most of the time... – Martynas Žiemys Feb 15 '19 at 9:49
• Is it updating during Render for you? There isn't any more detail to add about my render process i'm afraid. I am just hitting f12. – Ascalon Feb 15 '19 at 20:39
• OK, so that's some detail. You are not rendering from command line for example. How are you switching the scenes? Manually as well? As you can see in the GIF, it updates the location of a cube that is linked to both scenes. As it updates, rendering will be correct as well. I will try with materials. – Martynas Žiemys Feb 15 '19 at 22:48