Masked UI Element's Shader Not Updating

I have a custom shader I am trying to use on a UI Image element. When the element is rendered by itself, the shader works fine. If I make the UI Image a child of an object with a mask, the shader ceases to update unless I disable and reenable it again.

I have found this problem also happens with the default UI shader as well.

I am using Unity 5.2.3f1.

Here is an example of what’s happening:

The top row of images use my custom shader with an animated value.
The bottom row is using Unity’s default UI shader with an animated color element.

The left column shows unmasked elements.
The right column shows the same objects placed inside an element with a mask script on it.

Using “SetMateralDirty()” or “SetAllDirty()” on any of the masking elements on the right does not change the problem.

Has anyone encountered this problem before? I’ve tried looking around but haven’t found a solution. I’ve attached a copy of the project shown above to help anyone who might want to have a peek.

Cheers and thanks for the help -M

2407098–164425–SampleProject.zip (1.87 MB)

In diagnosing a bug in my project today, I’ve discovered I’m running into this exact same issue. I’m still on 5.2.2f1 on my end, but I was not experiencing the issue prior to that version (the relevant shader and MonoBehavior in question in my case I originally authored in August, so I’m guessing that’s 5.1.x timeframe).

EDIT: just double-checked, and yep everything works fine if I disable the UI mask component outright. Altering the “Show Mask Graphic” field (enabled in my original implementation) does not resolve the issue, however.

Did you ever make any progress on this? I’m still stuck. Had to work around it by removing the mask, but that shouldn’t be the solution.

So guessing here but pretty sure the issue is due to us copying the shader and making modifications to support stencils. My guess is that the material that you are referencing for the animation is not the one that is being used to draw (using the original vs the copied).

So which material would I need to grab in order to do the animation? Would that be the one on the parent with the image mask?

Okay, so it looks like Phil is correct. The reference is being broken in some way that I’m not very clear on. I did some poking around and found the SetGlobalFloat(string propertyName, float value).

By making the animated value in my shader private, I can then animate it as follows:

Shader.SetGlobalFloat("_FadeIntensity", lerpValue);

I only have one object on screen that needs to be animated, so this solution works for me, but it’s certainly not a workable solution of you need independent animations for multiple objects on screen.

2 Likes

This is really a problem that has to be resolved. Thanks michaelhill for the fast solution.

I got the same exact problem (not updating parameters in shader), but I instantiate a material as:

Image img = GetComponent<Image>();
m_mat = Instantiate(img.material);
img.material = m_mat;

So this solution is not going to work as it will change parameters globally.
Inside my c# code I call:
m_mat.SetColor(“_Color”, m_color);

Any ideas how to pass parameters to a shader with a stencil?

I’m experiencing the same issue…

It looks like a work around is by disabling the image, then re-enabling it right after

1 Like

Phil-Unity had it right but didn’t elaborate enough to keep you from just hacking around with the global parameters.

The Mask object causes a modified material to be rendered rather than the baseMaterial so it is no longer rendering the texture that you are updating with the “material” property. Using set on the materialForRendering property will set the correct material if a mask is involved.

So in your project adding

topImageMasked.materialForRendering.SetFloat(“_FadeIntensity”, lerpValue);
bottomImageMasked.materialForRendering.color = newColor;

8 Likes

Thank you so much! This had been giving me a lot of trouble, but using “materialForRendering” fixes my problem.

I have encountered the problem now as well. But .materialForRendering does not work in my case, since I need to change the properties while the mask is disabled and enable the mask later. Use .material or .materialForRendering does not make a difference, since .materialForRendering points to two different materials, the original and the stenciled copy, depending on the state the mask is in, and the copy is not updated when the original is edited.

I filed a bug-report (case 994413) in the hopes that this behaviour might be fixed in a future release.

Incase anyone’s looking for a solution- After setting the material’s property, simply disable and enable the graphic component in two lines : img.enabled = false; and then img.enabled = true;

4 Likes

I can’t believe this is the correct solution, but it absolutely worked for me. Is there no better way?

I guess it also begs the question, does disable/enable have any overhead?

1 Like

I have a ‘solution’, but I don’t know if it is better. I dug a bit in Unity’s open source CS Reference and made a small edit to MaskableGraphic.cs and built a custom dll. Sadly you cannot replace Unity Engine dlls in your Project, you have to replace them in the installation Folder.

It does not work in Unity 2019.2.1f1

Thanks for this thread.
In my project, I had the same issue, a material with a custom shader not updating when changing the value through code. After updating the value, I found that disabling / enabling the gameobject worked, but I also found this line of code that looks more elegant imo:MaskUtilities.NotifyStencilStateChanged(_maskReference);

4 Likes

known unity bug.

I have the same problem. Adding stencil support to a simple shader, results in the material not being editable in the inspector window… all the shader variables/properties become greyed out in inspector, and non-interactable.
The shader works just fine, but you have to edit the material separately, not on the game-object it is assigned to.

Yeah I had to force a UI refresh to get it working, and even then sometimes there is a flicker, etc.