# Blender 2.8 Python : How Do I Find My Material Output Node (and assign displacement)

I am attempting to create a displacement node with an attached image, and connect that to the Main Material Output Node.

I feel I understand how to create and connect these pieces (mostly) fine now. But I am unable to locate/reference the original Material Output, OR how to make a new Material Output the assigned one.

Please forgive my code, it is a hacked attempt between many forums posts and my own understanding after 1 week into python and blender.

Thank you in advance (code and pictures below)

Images:

What I want What I have

Code

#Import python
import bpy
from bpy import context, data, ops

# Clear all nodes in a mat
def clear_material( material ):
if material.node_tree:
material.node_tree.nodes.clear()

# Create a node corresponding to a defined group
def instanciate_group( nodes, group_name ):
group = nodes.new( type = 'ShaderNodeGroup' )
group.node_tree = bpy.data.node_groups[group_name]

#ref materials
materials = bpy.data.materials
#name material
mat_name = 'DELETE_THIS_CODE'
# get ref to material
material = materials.get( mat_name )

#if not our mat
if not material:
material = materials.new( mat_name )

# We clear it as we'll define it completely
clear_material( material )

material.use_nodes = True

nodes = material.node_tree.nodes

output = nodes.new( type = 'ShaderNodeOutputMaterial' )

diffuse = nodes.new( type = 'ShaderNodeBsdfDiffuse' )

#input = nodes.new( type = 'ShaderNodeTexImage')

#With names
#Or with indices

#______________________^^ multiple things above here might be deletable ^^_____________

#Create material named X
mat = bpy.data.materials.new(name="Mat_Tile")
#Set this material to use nodes
mat.use_nodes = True
bsdf = mat.node_tree.nodes["Principled BSDF"]

#reference selected object
ob = context.view_layer.objects.active

# Assign it to object
if ob.data.materials:
ob.data.materials[0] = mat
else:
ob.data.materials.append(mat)

#Make a normal map node

#Displace Img
#Attach imag to diplace node

#connect the Displacement node to the Material Output node

#uwrap node


The Material Output can be retrieved by name or by type. If you know the name of the node and the variable nodes contains a reference to the nodes of the material's node tree, then you can access the Material Output node through a look up where the name is used as key:

material_output = nodes.get("Material Output")


A material may contain more than one Material Output node or it may have been renamed. In this case you could search for a node that is of type OUTPUT_MATERIAL.

material_output = None
for node in nodes:
if node.type == "OUTPUT_MATERIAL":
material_output = node
break


In both cases it may happen that no Material Output node exists, for instance because you're working on an existing material where it was deleted. Therefore you need to check if material_output is None and if it is create a new node.

if material_output is None:


When you're creating a new material from scratch you do know that both the Principled BSDF and Material Output nodes exist and you can access them by name.

In order to connect two nodes you need access to the material's node tree links. Assuming the variable links references these, you can add new links through the new() function. The first argument is the input socket of a node and the second argument is the output socket of a node. Assuming that principled_bsdf would reference a Principled BSDF node and base_color an Image Texture node, you could connect them in the following way:

links.new(principled_bsdf.inputs["Base Color"], base_color.outputs["Color"])


The following code shows how to create your desired node setup by creating a new material which contains a Principled BSDF and Material Output node by default.

import bpy

# Define names and paths
material_name = "Example_Material"

# Create a material
material = bpy.data.materials.new(name=material_name)
material.use_nodes = True
nodes = material.node_tree.nodes

# Since you want a Principled BSDF and the Material Output node
# in your material, we can re-use the nodes that are automatically
# created.
principled_bsdf = nodes.get("Principled BSDF")
material_output = nodes.get("Material Output")

# Create Image Texture node and load the base color texture

# Create Image Texture node and load the normal map

# Set the color space to non-color, since normal maps contain
# the direction of the surface normals and not color data
normal_tex.image.colorspace_settings.name = "Non-Color"

# Create the Displacement node

# Connect the base color texture to the Principled BSDF

# Connect the normal map to the Displacement node

# Connect the Displacement node to the Material Output node


The following example is for editing an existing material:

import bpy

# Define names and paths
material_name = "Example_Material"

# In case you're modifying an existing material you
# will have to check whether or not it contains a
# Material Output node. If it doesn't it has to be
# created.
material = bpy.data.materials[material_name]
material.use_nodes = True
nodes = material.node_tree.nodes

# In case you don't know the name of the nodes you can also search
# by type. This can be useful when you edit an existing material
# where the nodes could've been renamed.

# Find the Material Output node, if it exists
material_output = None
for node in nodes:
if node.type == "OUTPUT_MATERIAL":
material_output = node
break

# Perhaps the nodes hasn't been found then you'll have to create it
if material_output is None:

# Create a Principled BSDF nodes

# Create Image Texture node and load the base color texture

# Create Image Texture node and load the normal map

# Set the color space to non-color, since normal maps contain
# the direction of the surface normals and not color data
normal_tex.image.colorspace_settings.name = "Non-Color"

# Create the Displacement node

# Connect the base color texture to the Principled BSDF

# Connect the normal map to the Displacement node

# Connect the Principled BSDF node to the Material Output node

# Connect the Displacement node to the Material Output node


If you want to assign the material to the active object in Blender 2.8x then the the following line can be added to either of the scripts above. We do not need to retrieve the material again, since we already have a reference to it in the variable material.

bpy.context.active_object.active_material = material

• You're response and details are very much appreciated. Thank you for your time, Robert. I had been researching a bit and struggled to find or understand the searching of nodes. – WillSmithsRobot Nov 13 '19 at 12:50
• Attempting the first method does not cause errors, but also does not work for me. _____________________________________________________________________________________ material_output = nodes.get("Material Output") _____________________________________________________________________________________ #connect the Displacement node to the Material Output node mat.node_tree.links.new(dNode.outputs['Displacement'], material_output.inputs['Displacement']) – WillSmithsRobot Nov 13 '19 at 12:54
• *** The second method does not work for this as well. The same issue occurs where the code runs fine, but no connection is made using code that does connect for other methods on the same nodes. _____________________________________________________________________________________ I am 100% positive there is a Material Output node because when I made code to create one, it was never applied to anything because one already existed. _____________________________________________________________________________________ I'm struggling to debug why MO would not be accessable with these – WillSmithsRobot Nov 13 '19 at 12:59
• The code you wrote for my specific setup does work. Unfortunately, it does not combine easily with the "attach to object" code I had. If you see this, I would be interested in the process to appropriately assign the material as well. Thank you. – WillSmithsRobot Nov 13 '19 at 13:17
• @WillSmithsRobot Sure I can add an example of how to assign a material to an object. – Robert Gützkow Nov 13 '19 at 13:50

With the Help of @Robert Gutzkow & a post from [here], I have a working-as-intended/needed script. Thank you for your help, and I hope this helps someone else too!

#Import python
import bpy
from bpy import context, data, ops

# Define names and paths
material_name = "Material_KitchenTile"

# Create a material
material = bpy.data.materials.new(name=material_name)
material.use_nodes = True
nodes = material.node_tree.nodes

# Since you want a Principled BSDF and the Material Output node
# in your material, we can re-use the nodes that are automatically
# created.
principled_bsdf = nodes.get("Principled BSDF")
material_output = nodes.get("Material Output")

# Create Image Texture node and load the base color texture

# Create Image Texture node and load the normal map

# Set the color space to non-color, since normal maps contain
# the direction of the surface normals and not color data
displ_tex.image.colorspace_settings.name = "Non-Color"

# Create the Displacement node

# Connect the base color texture to the Principled BSDF

# Connect the normal map to the Displacement node

# Connect the Displacement node to the Material Output node

#__________Assign the material

#Ref selected obj
ob = bpy.context.active_object

# Get material
mat = bpy.data.materials.get(material_name)

# Assign it to object
if ob.data.materials:
# assign to 1st material slot
ob.data.materials[0] = mat
else:
# no slots
ob.data.materials.append(mat)