Referencing material functions from inside a custom expression

Let’s say I have a material function that I like, does the job well and doesn’t need to be rewritten. But I want to progressively loop over it in a custom expression.

Can I access the material function in any way? By the looks of it they’re not compiled as fragment shaders and they’re simply inserted wholesale into statements.

1 Like

Interesting, would like to know that as well. My guess would be on “no” though.

Do for loops work in custom nodes yet?

Yup! For loops work. You can write regular hlsl or cg in the custom expression node. It can even call other custom expression node functions, but material functions really don’t seem to exist as anything other than inline statements. That’s a shame, because it’d be nice as an option to have them as fragments as well.

So far I have had to ‘inline’ the function into the custom node as hlsl code to get this to work. The reason is that the final hlsl code will simply call your function with the ‘inputs’ as simple variables that have already been processed. So there is no interface to tell the ‘input variables’ to re-evaluate from a different position (it would need to understand expression inputs for this to work I think).

Martin Mittring did something for volumetric decals to allow you to specify external input for raytracing. I am not sure what he did there but I have been meaning to find out.

FWIW, if you are having trouble converting material nodes to hlsl code, you can always build a small separate material and hook it up to emissive and then open up the preview HLSL code window and find/copy the area under emissive. In that way sometimes you can get some compiler optimizations as well.

That’s pretty much what I’ve been doing - checking out the generated statements and seeing what ended up where.

I think it’d be nice if there was a flag on material functions to output it as a fragment. This has an obvious performance impact, but it might be saved elsewhere as a result. That means we can keep building materials in the lovely blueprint editor rather than digging through HLSL every time there’s an error. The fragment itself can benefit from the UE inline-statement optimisations as well.

Of course, all of this is due to the fact we can’t do for(){} loops or switches in material blueprints. I sure don’t want to be unrolling loops by hand to optimise them, so why can’t the compiler do it for me in instances where the loops are relatively static? And at least some of this for me is because you still can’t do branching in post-process blendables due to a long outstanding bug preventing it.

I’m just starting to get into the code project side of things so I’ll take a look at this myself, but it’s clearly a feature people want.

Yes it should be possible to get support for that but I cannot say when it can be expected to happen. I have been talking to some programmers about it recently already.

The part of the engine mostly responsible for this is a file called HLSLMaterialTranslator as well as MaterialExpressionCustom.h. Mostly in the HLSLmaterialtranslator is a block where the ‘code’ statement gets evaluated and checked for the word ‘return’. You could probably add a quick hacky string replacement function somewhere in there to inject a custom input expression. Or maybe add some option handling for fragments like you talked about but the implementation details beyond that are beyond my programming skills. At least when it comes to doing things the ‘right’ way. It’s usually much easier to get something simpler that isn’t fully robust working though, which is why it can be hard for every feature to be officially supported.

Hmm, thanks for the hints! That gives me a place to start. I’ll post back in this thread in a week or two if I make some progress.

Hi Antidamage, is it possible to have an short snippet code example showing how you called custom expression node functions inside a custom expression node?

I’ve tried putting the following code in the Custom Node but it didn’t work:



float hi(float i)
{
   return i*3;
}

return hi(7);


The code you have above actually looks something like:


float4 fragment_shader(input, input, input, output) {
   float hi(float i)
   {
      return i*3;
   }


   return hi(7);
}

Custom expressions are always the guts of a fragment shader as the compiler wraps it in the appropriate syntax to make a function. The trick is to make separate custom expressions that are valid and evaluate fine, then open the HLSL code window, search for your fragment shader and call it. I don’t have my example handy but it does work.