How can I reference a relative file path in an #include in a custom node?

Hiyo! I’m attempting to smooth over the process of including HLSL files in my materials. So far what I’ve found is that I can store code for the Custom node in an HLSL file by adding in the custom node’s Code property:

#include "C:/Shaders/ShaderCode.hlsl"
return 0;

As you can see, the path is literal, which introduces a lot of issues with packaging and source control. Is there any way for me to use a relative file path or macro to include these files in source control and packaging? Preferably, the path would reference a path in the project folder.

I believe you’d have to make it relative to the Engine/Shaders folder, as that’s the path from which all the other #includes are resolved.

Be aware that what you’re doing is the equivalent of pasting the contents of that file into an HLSL function that the engine generates for each custom node when it’s translating the node graph into the code that gets passed to the shader compiler:

<ReturnType> CustomNodeN (FMaterialPixelParameters Parameters, <OtherParameters>)
{
    #include "your file"
}

That is to say, only code that would be valid in such a context will work inside your #include file. You could not easily define new functions inside that file, for example, as HLSL does not support nested function definitions. This method also has the drawback of making it more difficult to figure out what’s going on in the material later; you might be better off using material functions containing the custom nodes if you plan on reusing them over multiple materials. If your goal is just to have an file with a bunch of common shader functions you can call from your custom nodes, you could modify the MaterialTemplate.usf file to add the #include for your file at the top, so it’s included automatically with every material shader that’s compiled, though this will necessitate a recompile of all materials when you change that file.

For the custom version of the engine being used at our studio, we’ve added support for specifying a list of arbitrary #includes on the material (or material function) itself, to avoid changes to special-case stuff from forcing all materials to be recompiled. The list of files is specified on the material, and our artists then call the functions in those files from inside custom nodes. You could consider that route - it worked very well for us.