3
$\begingroup$

I am trying to make a setup where a different node is connected to a node group's output depending on what Render Layer is being rendered. For example, if layer 1 is rendering, then RGB is connected. If Layer 2 is rendering, then RGB.001 is connected.

I already have a setup that let's me use a Driver that outputs a different value based on which layer is rendering, so I think this ought to be possible.

Here is that script:

def layer_number(depsgraph: bpy.types.Depsgraph):
name = depsgraph.view_layer.name

try:
    layer_no = int(name.split(':')[0])
except:
    layer_no = 0
    
return layer_no

This is based on this feature that was added in 2.9(scroll to bottom) that allows things from the dependency graph to be accessed in Drivers.

To use it, you create a Driver on a value with the expression layer_number(depsgraph). The value will then be a number you put at the start of the Render Layer name. This can then drive a mix node, or whatever else. It will update when switching layers in the viewport, or when F12 rendering. (The script needs to be registered or run of course.)

For various complex reasons that are too long and specific for this post, I can't use just this method of mixing between two different nodes. I need to plug in one or the other. I have a script that removes or creates links as needed, but I need to be able to trigger it and feed it the proper value based on which Render Layer is currently rendering.

So, the script needs to do something like this:

  1. Run each time a new Render Layer comes up, either in the viewport or during a full F12 render.
  2. Get a Value from the Dependency graph based on the Render Layer that's about to be rendered.
  3. Run the script/function that changes the node links.
  4. Proceed with the Render.

Is this possible? I am aware that there are ways to trigger things at the start or end of a render job, but generally that means before and after rendering all layers, and won't let you change things per layer. But since the above Driver feature was added, I am now thinking that you can change things per layer, even during a larger render job?

(I am aware that you can write command line batch render scripts that do a render job of an individual layer, then change something, then do a render job of the next layer. I am looking for solutions beyond that, as I want this to work when switching layers in the viewport, or rendering only individual layers in the compositor, etc.)

$\endgroup$
3
  • $\begingroup$ do you mean like this? youtu.be/IDSbo5QpVu4. if yes, i first would like to verify whether it works with rendering. How can i render different layer views to different png? $\endgroup$
    – Chris
    May 29, 2021 at 13:09
  • $\begingroup$ i made it now successfully for rendering, but the viewport update doesn't work always. I have no idea why. $\endgroup$
    – Chris
    May 31, 2021 at 7:42
  • $\begingroup$ @chris cool. Viewport updates can be unreliable in the Driver mix version too. $\endgroup$
    – Ascalon
    May 31, 2021 at 7:59

1 Answer 1

4
+50
$\begingroup$

there might be more elegant solutions, but looks like mine works now ;)

the driver (i just put in on x-location for testing) looks like this:

enter image description here

The driver is responsible for the viewport render changes.

and this:

bpy.app.handlers.frame_change_post.append(my_handler)

is responsible for the render frame changes (F12).

Here is my python script:

import bpy
from datetime import datetime
import time
from bpy.app.handlers import persistent
from bpy.app import driver_namespace

def layer_number(depsgraph: bpy.types.Depsgraph):
    
    name = depsgraph.view_layer.name
    
    print("name:", name)
    
    try:
        layer_no = int(name.split('.')[1])
    except:
        layer_no = 0
        
        
    print(layer_no)
    
    mat = bpy.data.materials.get('mat')
    nodes = mat.node_tree.nodes
    
    output = nodes.get("Material Output")
    
    links = mat.node_tree.links
    
    if layer_no == 1:
        a = nodes.get("a")
        link = links.new(a.outputs[0], output.inputs[0])
        print("a")
    else:
        b = nodes.get("b")
        link = links.new(b.outputs[0], output.inputs[0])
        print("b")
        
    return layer_no


def my_handler(scene, depsgraph):
    print("Frame Change -------------->", scene.frame_current)
    layer_number(depsgraph)
    

bpy.app.handlers.frame_change_post.append(my_handler)

print (bpy.context.view_layer.name)


@persistent
def load_handler(dummy):
    print("Load Handler")
    #dns = bpy.app.driver_namespace
    # register your drivers
    bpy.app.driver_namespace['layer_number'] = layer_number


def register():
    print("Register")
    load_handler(None)
    bpy.app.handlers.load_pre.append(load_handler)
    bpy.app.handlers.load_post.append(load_handler)


register()


    

blend file to check it out:

To test:

  1. run the script

  2. change to viewport shading rendered

  3. change the view layer from "View Layer.002" to "View Layer.001" and back

  4. render it out

if you have questions, just ask. Then i will improve my answer.

Result:

enter image description here

enter image description here

enter image description here

i did composite it that way so you see that the material really changed.

$\endgroup$
1
  • $\begingroup$ Can you tell me what exactly the @persistent is being used for? I've made an altered version of this script, and it seems to work just fine without that. $\endgroup$
    – Ascalon
    Jun 29, 2021 at 3:17

You must log in to answer this question.

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