Masked Material Shader Changes not working correctly

I regularly run into the same problem currently:

I have a Material on an UI-Image that is masked by a parent with a Mask Component. Now I want to change some Material Properties via script (SetTexture, SetFloat, etc). I know that there is a new-ish ‘MaterialForRendering’ property, especially for masked Materials, but for some reason, Unity is unable to use this properly. When I edit properties before turning the Mask on, they are not applied to the masked material, and vice versa. (When I enable the Mask for the first time, the material is copied correctly, but on all subsequent enable/disables, the materials seem to be handled completely separate.)

I already filed a bug report weeks ago (994413), but it was closed because apparently it is “its not the common case” to want to enable/disable a Mask after changing material properties during runtime.

An additional problem is that we create all our UI via code, not buy building it in the Editor, so even if I never enable/disable a Mask, I cannot guarantee that material changes happen always after Unity has copied the Material for masking. I can do the changes after parenting, but the masking/material-copying seems to happen sometime during rendering, so I have to make some changes to the ‘material’, but later ones to the ‘materialForRendering’.

My current ‘workaround’ is to never disable/enable masks (which brings some limitations to how my application looks) and duplicate every change manually like so (which is stupid):

img.material.SetFloat("Name", value);
img.materialForRendering.SetFloat("Name", value);

Is there a known fix that actually works, or is my use-case just too outlandish?

2 Likes

I just now found a possible solution.

I noticed that the masked material will be updated corerctly when I disable the object and enable it again. So I got the UI Repository from Bitbucket and had a look at MaskableGraphic.cs. I copied the following two lines from OnDisable() to RecalculateMasking(), compiled the solution into a dll and replaced the one in my unity installation:

StencilMaterial.Remove(m_MaskMaterial);
m_MaskMaterial = null;

I do not know if that produces problems elsewhere, though.

2 Likes

I have the same problem!Are you solved now?

The following code should work:

            //We have to disable and enable the mask component
            //as we changing the default shader, the
            //mask did not refresh the shader for the masking
            //components.
            //MaskUtilities.Notify2DMaskStateChanged and
            //MaskUtilities.NotifyStencilStateChanged did not work!
            // https://discussions.unity.com/t/607569
            // https://discussions.unity.com/t/694722/2
            //Caution: alloc
            Mask[] masks = GameObject.FindObjectsOfType<Mask>(true);
            if ((masks != null) && (masks.Length > 0)) {
                for (int i = 0; i < masks.Length; i++) {
                    Mask m = masks[i];
                    bool oldEnabled = m.enabled;
                    m.enabled = false;
                    m.enabled = true;
                    m.enabled = oldEnabled;
                }
            }
1 Like