LoginSignup
2
1

More than 1 year has passed since last update.

[Unity]uGUIのデフォルトシェーダーをカスタムしたが、マスク対象のuGUIに反映されない問題

Posted at

問題

 Canvas.GetDefaultCanvasMaterialで取得できるマテリアルのシェーダーを置き換えることで、uGUIにカスタムしたシェーダーを使うことができます。

ChangeUIShader.cs
Canvas.GetDefaultCanvasMaterial().shader = customShader;

 一部シーンでSRPを使っている関係で、ゲーム中に動的にGetDefaultCanvasMaterialのシェーダーを置き換えていたのですが、MaskコンポーネントでマスクされたuGUIのシェーダーが置き換え前の初期uGUIシェーダーになってしまい、表示が破綻してしまいました

解決方法

上記のコードでシェーダーを置き換えた後、下記処理を入れることで解決しました。

ChangeUIShader.cs
StencilMaterial.ClearAll();
foreach (var canvas in FindObjectsOfType<Canvas>().Where(x => x.isActiveAndEnabled))
{
    canvas.enabled = false;
    canvas.enabled = true;
}

解説

マスクできるuGUIは全てMaskableGraphicを継承しています。
MaskableGraphicは、マスクされたときにGetModifiedMaterialメソッドが呼ばれ、その中で現在のuGUIのマテリアルを基にして、マスクされた時用のマテリアルをStencilMaterial.Addで生成しています。
GetDefaultCanvasMaterialを置き換える前にマスク用のマテリアルが作られてしまうと、GetDefaultCanvasMaterialを置き換えた後もそのマテリアルが使われるため、これが原因で表題のようなことが起きていました。

対策として入れたコードは、UIのデフォルトマテリアルのシェーダー置き換え前のものが残ってしまっているところを、クリア&全部Graphic.SetMaterialDirty呼ぶことで解決しているものです。

社内の有識者に教えてもらったんですが、コンナノワカラナイヨ…!

余談:Unityの標準コンポーネントのコードってどこで読むの?

今回だと、MaskableGraphic.csを読むことで原因が分かったんですが、標準コンポーネントのソースコードってどこで読むのが正道なんですかね…?

URPをインポートしてるプロジェクトでMaskableGraphic.csを検索にかけたら Librarly フォルダ内の下記の場所に格納されてて読めたんですが、こういう調べ方がセオリーだとは思わないので、次似たようなことが起きたときのために参照すべき場所を知っておきたい…。

Libraly/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/

参考:MaskableGraphic.GetModifiedMaterial

MaskableGraphic.cs
/// <summary>
/// See IMaterialModifier.GetModifiedMaterial
/// </summary>
public virtual Material GetModifiedMaterial(Material baseMaterial)
{
    var toUse = baseMaterial;

    if (m_ShouldRecalculateStencil)
    {
        if (maskable)
        {
            var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
            m_StencilValue = MaskUtilities.GetStencilDepth(transform, rootCanvas);
        }
        else
            m_StencilValue = 0;

        m_ShouldRecalculateStencil = false;
    }

    // if we have a enabled Mask component then it will
    // generate the mask material. This is an optimization
    // it adds some coupling between components though :(
    if (m_StencilValue > 0 && !isMaskingGraphic)
    {
        var maskMat = StencilMaterial.Add(toUse, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);
        StencilMaterial.Remove(m_MaskMaterial);
        m_MaskMaterial = maskMat;
        toUse = m_MaskMaterial;
    }
    return toUse;
}
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1