Creating And Manipulating Nodes For Cycles And Eevee Materials With Python

Manipulating Material Nodes in Python

Material nodes in Blender can be accessed and manipulated through Python scripts to automate material creation and modifications. The Blender Python API provides methods to retrieve node trees attached to materials, add and connect shader and texture nodes, change node settings, and animate property changes over time.

Accessing Material Nodes

To access a material’s node tree, the node_tree property of the Material datablock can be used. This will return a NodeTree datablock which contains references to all the nodes in the tree.

Getting material node trees

Material node trees can be accessed from Blender Python in a few ways:

  • By getting the node_tree property from an existing Material datablock
  • By creating a new material with nodes enabled and accessing its node_tree property
  • By creating a new NodeTree datablock directly and linking it to a Material

For example:

import bpy

# Get node tree from existing material 
material = bpy.data.materials['Material']
node_tree = material.node_tree 

# Create new material with nodes
mat = bpy.data.materials.new(name="MyMaterial")
mat.use_nodes = True
node_tree = mat.node_tree

# Create separate node tree and link to material
node_tree = bpy.data.node_groups.new(type="ShaderNodeTree", name="NodeTree")
mat.node_tree = node_tree

Accessing specific nodes

The NodeTree datablock contains a collection of all the nodes in the material node tree. These can be accessed a few ways:

  • By name – node_tree[“NodeName”]
  • By type – node_tree.nodes[ShaderNodeBSDF]
  • By index – node_tree.nodes[0]

This returns a Node datablock for each node containing settings and properties that can be modified.

# Get specific nodes
diffuse_node = node_tree.nodes["Diffuse BSDF"]
texture_node = node_tree.nodes["Image Texture"] 

Creating New Nodes

New nodes can be instantiated and added to node trees using the node type constants available in the bpy.types module.

Instantiating node trees

Empty node tree datablocks can be created by instantiating a new node tree:

node_tree = bpy.data.node_groups.new(type="ShaderNodeTree", name="MyNodeTree")

This creates a reusable node group that can be linked to materials, or individual nodes can be instantiated and added to existing trees.

Adding shader and texture nodes

Common shader nodes like BSDF, Vector Math, and Mix nodes can be created and added using the add_node method:

# Shader nodes
diffuse_node = node_tree.nodes.new(type=bpy.types.ShaderNodeBsdfDiffuse)
glossy_node = node_tree.nodes.new(type=bpy.types.ShaderNodeBsdfGlossy)  

# Texture nodes
texture_node = node_tree.nodes.new(type=bpy.types.ShaderNodeTexImage)  
noise_node = node_tree.nodes.new(type=bpy.types.ShaderNodeTexNoise)

This creates new node datablocks for each type, containing all the settings and properties to control their behavior and shader influence.

Connecting Node Sockets

For nodes to pass information between each other, their input and output sockets need to be connected.

Linking node inputs and outputs

Node sockets can be linked by accessing the input and output sockets of node datablocks directly. The link_make method creates a connection between sockets.

node_tree = bpy.context.active_object.active_material.node_tree

diffuse_node = node_tree.nodes["Diffuse BSDF"]
glossy_node = node_tree.nodes["Glossy BSDF"]

node_tree.links.new(diffuse_node.outputs[0], glossy_node.inputs[0])  

This connects the two shader nodes so they both contribute to the material, with the diffuse node feeding into the normal input of the glossy node.

Setting node input values

Input socket values can be set easily by accessing node properties:

texture_node.image = bpy.data.images["TextureImage"] 
bump_node.inputs["Strength"].default_value = 0.5

Connecting value and color nodes to inputs is a common way to drive shader properties.

Modifying Node Attributes

Once nodes have been added, their settings and attributes can be changed to alter how they impact the material.

Changing node settings

Common properties like position, size, label and naming can be altered:

noise_node.location = (100, 300)
noise_node.width = 300
noise_node.label = "Noise Texture"
noise_node.name = "NoiseTex" 

Changing properties like blend modes, roughness levels, or enabled inputs drives how the node operates:

  
glossy_node.distribution = "BECKMANN"
glossy_node.inputs[1].default_value = 0.2

Enabling/disabling nodes

Nodes can be enabled and disabled to turn their influence on/off:

noise_node.mute = True
bump_node.mute = False

This allows selective disabling of nodes while keeping connections intact, for fast iteration and testing.

Animating Material Changes

Material nodes can be animated by inserting keyframes on properties and graph/node relationships.

Setting keyframes on properties

Any visible node property can be animated with the insert_keyframe method:

strength_node = node_tree.nodes["Math Multiply"]
 
# Set start value  
strength_node.inputs[1].default_value = 2
 
# Insert keyframes 
strength_node.inputs[1].default_value = 5
strength_node.inputs[1].insert_keyframe(frame=10)

strength_node.inputs[1].default_value = 1  
strength_node.inputs[1].insert_keyframe(frame=100)  

This interpolates the multiply value from 5 to 1 over the animation.

Animating node connections

Links between nodes can also be animated to smoothly transition relationships:

# Link nodes
links = node_tree.links
links.new(node1.outputs[0], node2.inputs[0])
  
# Keyframe link insertion  
links[-1].insert_keyframe(frame=10)
  
# Add new node and reconnect
node3 = node_tree.nodes.new(...) 
links.new(node1.outputs[0], node3.inputs[0])
links[-1].insert_keyframe(frame=100)

This transitions node connections from node1->node2 to node1->node3 over the animation range.

Example Scripts

Some example scripts for automating common material node workflows in Python include:

Glossy to diffuse transition

Smoothly crossfades between glossy and diffuse shaders:

# Keyframes for Mix factor
mix_node.inputs[0].default_value = 1.0   
mix_node.inputs[0].insert_keyframe(frame=1)

mix_node.inputs[0].default_value = 0.0
mix_node.inputs[0].insert_keyframe(frame=100) 

Cyclic noise texture

Automates animated noise patterns using drivers:

# Add driver to noise node offset  
fcurve = noise_node.inputs[2].driver_add("default_value")

# Set driver expression
fcurve.driver.expression = "sin(frame/10)*0.5" 

This causes the noise texture input to continually shift using a cyclic sine function.

Leave a Reply

Your email address will not be published. Required fields are marked *