2
$\begingroup$

I am trying to create a material node group for triplanar mapping, but I am not sure how I can parameterize the image texture name.

I have 3 image texture nodes, which all use the same file with the same settings, the only difference is the vector input.

Is there a way I can make it so that I only have to select the texture name once, possibly as a input for a group?

Example

$\endgroup$
1
$\begingroup$

This can be achieved with OSL - but this is not compatible with GPU rendering - so CPU rendering only.

To start with we need a node where we can specify the filename. For this we can use the following OSL code - paste it into a new Text Editor datablock named 'simpleFilename.osl' :

shader simpleFilename(
    string FileName = "",
    output string oFileName = "<undefined>"
    ) {
    oFileName = FileName;
}

In the Node Editor, add a Script node and set point it at your new 'simpleFilename.osl' text block. Assuming you have OSL enabled (if it's available in your Blender then you'll have an Open Shading Language checkbox on your Render properties) you should see the node transform into the following :

simpleFilenameNode

This new node provides an input (which can either be connected to another 'string' socket or the filename can be typed into the input field - enter the full path to the image or use a '//' prefix to indicate a 'local' file relative to the location of the saved .blend file) and an output - both of which should be colored white to indicate type 'string'. The output from this node will be used to drive our Image Texture node (so you can drive multiple Image Texture nodes from the same input).

However, while we're at it we might want to add some flexibility - we can use OSL to combine multiple inputs to generate the filename. For example, create another Text block , this time named 'dynamicFilename.osl' and paste the following :

shader dynamicFilename(
    string Base = "//",
    int Index = 0,
    int Digits = 4,
    int Subframe = 0,
    int SubframeDigits = 4,
    string Suffix = ".png",
    output string oFileName = "<undefined>"
    ) {
    string filenameFormat;
    if (Subframe > 0) {
        //Build format string as "%s%0<Digits>i_%0<SubframeDigits>i%s"
        filenameFormat = concat("%s%0",format("%i",Digits),"i_%0",format("%i",SubframeDigits),"i%s");
        oFileName = format(filenameFormat, Base, Index, Subframe, Suffix);
    }
    else {
        //Build format string as "%s%0<Digits>i%s"
        filenameFormat = concat("%s%0",format("%i",Digits),"i%s");
        oFileName = format(filenameFormat,Base, Index, Suffix);
    }
}

Assigning this to a Script node will produce a node with additional inputs :

dynamicFilenameNode

This node will behave similarly to the 'simple' version except that the filename will be built from the various elements in the formal PPPPNNNN_MMMMSSSS where PPPP is the prefix (eg, //images/), NNNN is a numeric representation of the Index input value (formatted to Digits number of digits - eg, '0001'), MMMM is similar but based on Subframe (and if Subframe is zero then the _MMMM element is omitted entirely), and SSSS is the filename suffix (eg, .png). In this way you can construct a filename that is automatically generated based on the input values similar to that used in How to show different images based on Object ID.

Now all that is required is the actual Image Texture and this is achieved by using the OSL 'texture' function which can extract pixel data from an image. Create another Text datablock (named 'imageTexture.osl') and paste the following :

shader imageTexture(
    vector Vector = P,
    string FileName = "",
    int Wrap = 0,
    output color Color = color(1,1,0)
    ) {

    string wrap;
    if (Wrap > 0)   { wrap = "periodic"; }
    else            { wrap = "black"; }

    Color = texture(FileName, Vector[0], Vector[1], "wrap", wrap);
}

Assigning this script to a Script node will produce the following :

imageTextureNode

Note the FileName input to accept the filename (as generated by either the 'simpleFilename' or 'dynamicFilename' node) along with a Wrap input (the equivalent to Repeat/Clip of the standard Image Texture node - set to '0' for Clip, 1 for Repeat).

Now simply connect the nodes as required, linking the output oFilename to each of the ImageTexture's FileName input :

example

Varying the inputs to the 'Filename' node will vary the image for each of the Image Textures. Each Image Texture will be using the same image and this can be amended by simply changing the Filename node. The filename can also be passed into and out of node groups.

$\endgroup$

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.