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.