I am relatively new to blender and its python API and I am trying to define a reflective material similar to what was described in this post: "How to make a "reflective tape" material in cycles?"

Picture from above mentioned post:

enter image description here

As I have understood it so far you need to define a node_tree with shader nodes and their connections:

mat = bpy.data.materials.new("reflect")
mat.use_nodes = True
tree = mat.node_tree
tree.nodes.remove(tree.nodes["Principled BSDF"])  # remove the default shader

# Define nodes

# Material Output
output_node = tree.nodes["Material Output"]

# Mix Shader
mix_node = tree.nodes.new(type="ShaderNodeMixShader")

# Diffuse BSDF shader
diffuse_node = tree.nodes.new(type="ShaderNodeBsdfDiffuse")

# Glossy BSDF shader
glossy_node = tree.nodes.new(type="ShaderNodeBsdfGlossy")

# (1) How to define geometry_node?
geometry_node = ???

# Define node connections

# (2) how to properly define a connection between geometry_node's incoming and glossy_node's normal attribute?
tree.links.new(geometry_node.outputs["Incoming"], glossy_node["Normal"]) ??

tree.links.new(glossy_node.outputs["BSDF"], mix_node.inputs[1])

tree.links.new(diffuse_node.outputs["BSDF"], mix_node.inputs[2])

tree.links.new(mix_node.outputs["Shader"], output_node.inputs["Surface"])

# (3) How to define node attributes such as Roughness and Fac and set Glossy type to GGX?

For clarity here are my questions:

  1. How do you define a geometry node where incoming can be used as output? Similarly to what is defined by the Blender GUI graph above.
  2. How do you properly define a connection between geometry_node's incoming output and glossy_node's normal input?
  3. How do you define node attributes such as Roughness and Fac and set Glossy type to GGX?

Also, let me know if I have missed anything in the above code.



1 Answer 1


Create the node of interest manually, press ⬆ ShiftF4 to switch to console, paste the following code:

C.area.type = 'NODE_EDITOR'; node = C.active_node; C.area.type='CONSOLE'; print(type(node))

press ↩ Enter to confirm and see what was printed. In case of the "Geometry" node: <class 'bpy.types.ShaderNodeNewGeometry'>. To return to the node editor, press ⬆ ShiftF3.

As for connections, they are called "links" and are accessed similarly to nodes, which your code already shows. The node_tree.links.new() takes an output socket object first (because that's the start of the link), and then an input socket object (that's where the link leads to). It seems you're doing this correctly, unless your indices are wrong. A less error prone way to access sockets is by name, but here multiple sockets have the same name - you could use a list comprehension to gather all sockets with that name and then just access the 1st/2nd element of the resulting list…

As for your 3rd point, "defining attributes" is called "setting a default value", as the values you can see on nodes are "default values" used in the case nothing is connected to them. You change them by writing to the default_value attribute of an input socket.

And finally, in order to figure out how to change the option of the node, you can repeat the ⬆ ShiftF4 trick to find out the type of this node: bpy.types.ShaderNodeBsdfGlossy, copy-paste it to google and find the attribute in the docs:


Or after applying this trick you could take advantage of the node name still holding the reference to the node object, type this name, followed by a dot and ⭾ Tab to ask the console for suggestions:

This gives you quite a lot of attributes to test, but is a great way to learn (that's how I learned bpy, mostly).

So your GPT's (😜) code becomes:

import bpy

mat = bpy.data.materials.new("reflect")
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.remove(nodes["Principled BSDF"])

output_node = nodes["Material Output"]
mix_node = nodes.new(type="ShaderNodeMixShader")
diffuse_node = nodes.new(type="ShaderNodeBsdfDiffuse")
glossy_node = nodes.new(type="ShaderNodeBsdfGlossy")
geometry_node = nodes.new(type="ShaderNodeNewGeometry")

links.new(geometry_node.outputs["Incoming"], glossy_node.inputs["Normal"])
shader_inputs = [i for i in mix_node.inputs if i.name == 'Shader']
links.new(glossy_node.outputs["BSDF"], shader_inputs[0])
links.new(diffuse_node.outputs["BSDF"], shader_inputs[1])
links.new(mix_node.outputs["Shader"], output_node.inputs["Surface"])

glossy_node.distribution = 'GGX'  # GGX is the default at least in my version of Blender
glossy_node.inputs['Roughness'].default_value = .1

I tested it on Blender 3.5.

  • 1
    $\begingroup$ Thank you for the very detailed answer! I was able to recreate the material in question for my project using the code you provided. Best regards from a human who regrets putting the term 'bot' in their username :P $\endgroup$
    – LooBot22
    May 31, 2023 at 11:01

You must log in to answer this question.

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