I am creating a material using a CustomShaderNodeGroup. The inner node tree has a texture, so I want to expose the texture parameters to the node UI. For doing so, I look for the texture inside the node tree and directly expose the different properties. But there is still one missing part I;m not sure how to do it. At the moment I have the following UI for my custom node:

enter image description here

As you can see, i have a similar UI as the original ImageTexture node, but I'm missing the open/new buttons and also the use fake user and all that stuff for the image itself as in the following image

enter image description here

With my current UI, the user can't open a new image, so this is a strong drawback. How could I make something more similar to the original ImageTexture UI?



For ID types like image use the template_ID method. As seen by viewing source of the image editor space, where sima = context.space_data

layout.template_ID(sima, "image", new="image.new", open="image.open")

What do you mean with the image editor space?

I mean look in blender for UI that matches, in this case I've arbitrarily used the header of the image editor.

enter image description here

With developer extras turned on if right click on open button, select view source the text editor will open code and point to the code above. This is the image set to the image editor.

In your case, going by answer simply change sima with env. Will work for any blender object that has an image attribute that points to an image bpy.types.Image

Give some class an image

Can add an image to your custom class

class Foo:
    image : bpy.props.PointerProperty(type=bpy.data.Image)

    def draw(self, context):
        layout = self.layout
        layout.template_ID(self, "image", new="image.new", open="image.open")
  • $\begingroup$ What do you mean with the image editor space? $\endgroup$
    – jjcasmar
    Dec 26 '19 at 16:39
  • $\begingroup$ Although I found a way to solve this very similar to what you proposed, I must give credit for the extra tips you mentioned. I will mark this as the accepted answer. $\endgroup$
    – jjcasmar
    Dec 27 '19 at 19:33

So there is a way to expose the image properties to a CustomShaderNodeGroup. Lets say we have a NodeGroup with one texture called "tex1". In the CustomShaderNodeGroup, set the node_tree attribute. We will use this node_tree to look for the texture on the draw_buttons function.

# Setup the node - setup the node tree and add the group Input and Output nodes
    def init(self, context):

On the draw_buttons function, look for the texture node and expose its properties using layout.prop and layout.template_image functions.

    def draw_buttons(self, context, layout):
        box = layout.box()
        env = self.node_tree.nodes['environment']
        box.label(text="Environment map")
        box.template_image(env, "image", env.image_user, compact=False, multiview=True)
        box.prop(env, "interpolation", text="")
        box.prop(env, "projection", text="")
        box.prop(env, "extension", text=""

When the user modifies this properties, the inner texture will update accordingly. Its not exactly the same UI, but its close enough to work with.

enter image description here

enter image description here


So far it's not possible to do that in Blender. Though it's been asked to the devs plenty times and I hope they will make something for this at some point.

In the meantime, shaders creators usually use two methods to go around this problem:

The first one is to make two node groups, the texture nodegroup and the shader nodegroup. The texture nodegroup contains only the image texture node, and the shader nodegroup contains the custom shader with the texture nodegroup used inside.
That way, when a user wants to edit the texture, they can do so just by editing the first nodegroup's content. That texture nodegroup can be duplicated and placed anywhere, if you change the texture inside, the change will be made in every instance of this nodegroup.

double level nodegroup

Another way is to just correctly organize your custom shader with reroutes, layouts and labels so that the user can understand and edit without a headache.

The two methods works, the first one is usually used when the nodegroup is way too mutch complicated and spaghetti-messy-looking to use the second method. (My example shader would be typically a simple one who doesn't need this encapsulated texture node.)
None of those solutions are perfect and it would be better to be able to put whathever we want in nodegroups inputs/outpouts, but that's all we can do for now.

  • $\begingroup$ I have found a solution for this. Create a CustomShaderNodeGroup. Assign the node_tree to the node_tree you need and find the texture inside. Expose the image properties to the CustomShaderNodeGroup. Ill write a proper answer for this $\endgroup$
    – jjcasmar
    Dec 26 '19 at 16:27
  • $\begingroup$ (I missed the python tag, don't tell anybody) $\endgroup$
    – L0Lock
    Dec 27 '19 at 10:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.