Disable Material feature by distance

Hello, I am trying to disable different material features by distance like WPO or overlay normals so I can get rid of a few instructions.

For this purpose I was trying to branch the material code by using linear interpolation or the if node, hence I could choose whether I want a feature to be render or not. However, after a bit of research I found out Unreal does not support dynamic branching (at least natively) and DynamicBranching node does not work neither as it simply calculates every single branch without taking account of the conditions.

Also, I saw a post that suggests to use a hlsl custom node where you explicitly tell Unreal you want to branch the code, and it seems it should be working.

[branch]
if (A>=B)
{
return BranchA;
}
else
{
return BranchB;
}

Unfortunately in my case I am not getting the expected results, even if I can see the material changing by distance as you get far from the object.

I write this post with hope someone can explain a little bit more about this particular problem and maybe find a workaround.

Thank you.

As far as I know, in order for dynamic branching to work in the material editor, all the code for all the branches needs to be inside the custom node.

If you’re just looking to disable WPO, static mesh components have a “WPO Disable Distance” setting in recentish (5.2?+) versions of UE5.

2 Likes

Hi, I’ll definetly give it a try for sure because right now I am simply plugin in the logic I create using nodes.

Yeah I saw this option and I will take it into account in future projects. Unfortunately right now I am currently working in a company project and we are using UE 4.27.

Thank you so much for your quick response :slight_smile:

1 Like

For what it’s worth if you want to see some examples of this implemented by Epic, the ParallaxOcclusionMapping node (it’s just a material function, after adding it to the graph you can double click it to open it up and see what it’s doing) uses a custom node to perform a branching loop and it should behave correctly

Edit: RayMarchHeightMap also has a branch. There are more I’m sure but those are the two I recall off the top of my head.

I’ve always just used a simpler material for further LODs for situations like this.

1 Like

Hello, yeah that is one of the alternatives I was thinking about, however we would be too limited and there is a risk of props starting to pop in.

Nevertheless, if the dynamic branching become impossible we’ll go with LOD solution I guess.

Thank you mate.

I have tried to make two different materials testing the logic inside the custom node and outside of it and the performance peak is indeed noticeable in RenderDoc. This of course confirms what you said about keeping everything inside the hlsl code.

However, I’m not sure if this approach is scalable for more complex materials like world position offset, layering system or overlay normals as you have to code all of this features instead of using nodes.

I would be pretty grateful if someone could shed some light on this if there is a way to avoid the coding workaround.

I’m wondering how much you’ll really save with this.
GPUs are notoriously slow when dynamic branches can go one way or the other for individual pixels, because they need to re-run the entire shader for the “other branch.”
Meanwhile, objects that are in the distance, have smaller screen coverage, so it’s not that many pixels that get shaded by them.
I would go with the LOD approach. If you’re worried about popping, then fade out the WPO using lerp in the closest LOD when it gets close to the transition distance.
But also, make sure that all this extra authoring effort actually matters.

1 Like

Yeah at this point I think probably the most reasonable approach is to use the LODs to change between material instances. Nonetheless, I am still pretty attracted to the idea of having very complex materials (e.g. a material layering system) that allow to disable different features by distance with a low impact in GPU.

I have to admit I saw this post (last GIF) about how they fade out material features in The Last of Us Part 2 and I thought it would be straightforward to implement in Unreal.

Thank you for your help.

This was patched to just work off regular ifs in something like .17 or so.
If there has been a regression or not, one cant be sure without testing.
BUT
As of .22 when I first read that flame war topic on the subject - topic which I bet epic has since decided to delete because information for us users is a dangerous thing. As everyone knows we could start asking nasty questions about epic’s stupidity… - the issue had been stably resolved.

Older shaders that used custom were left as is in the classic epic way.
(It works, dont touch it).

This fact doesn’t mean that you shouldnt still just use the custom node.
Doing so is future proof.
Using the regular if you take a gamble on when Epic f*s stuff up next.
And if today it works, there’s always tomorrow :wink:

Even if from reading it seems that even today it just doesn’t work right anymore…

Disabling features by distance is not a novel concept. Burks was doing so in some example water materials way back in 2020 or even before.

Generally it works well on instanced objects and such, but not on large objects like landscapes where both branches have to be calculated anyway.

A work-around for that was to limit the UVs where the branch occurs.
Basically.
Up to 10m you have UV0 custom with whatever up close and personal texturing/effects.
10m to 40m you have uv1 custom with less but not notning.
40m on you have uv3 custom with a whole lot less or next to just a texture.

Now, as of .22 you could do this with just IF statements.
If you have go go into to a custom node now i cant be sure.

Hoerver the trick was using the UVs rather than making calcs on the spot.

1 Like

When using the LOD system to change materials, be aware that you can cause an extra draw call and may negate some of the benefit. You can use the same mesh for both LODs if you only want to change the material and have no pop-in, but usually you can squeeze in some decimation while you’re at it.

1 Like

Good point, I will try to mitigate the drawcall issue by using HierarchicalInstanceMeshes, I guess it should batch them together taking LODs into account.