こんにちは、Unityエンジニアのオオバです。

お悩みさん
お悩みさん
  • Unity UIでソフトマスクを使いたい
  • ソフトマスクはできないと聞いてたけど最近はどう?
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    「ソフトマスク」とは以下の画像の通り 少しずつ透明になっていくマスク表現 です。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_0

    ↑の画像は女の子の顔を中心に円形のソフトマスクを適用したサンプルです。

    ※このイラストアセットは中世ファンタジーRPG風2Dキャラクターパックを使わせていただいてます。

    ゲームUIをデザインをする際には表現の候補に入るのではないでしょうか。しかし、Unity UIのマスク表現には課題がありました。Unityが未対応で 「ソフトマスクが使えなかったこと」 です。UIデザイナーのみなさま朗報です。 Unity2020からソフトマスクが使えるようになりました!!

    本記事ではUnity2020から使えるようになったソフトマスクを紹介します。ただしこのソフトマスクは条件付きです。
    でも、安心してください。 条件を満たさない場合の代替方法も合わせて紹介 します。この記事を読むだけでソフトマスクは実現できると思いますので、ぜひ最後まで読んでみてください。

    ソフトマスクはRectMask2Dで簡単実装

    Unity2021からRectMask2Dが進化しました。RectMask2DとはUnityのコンポーネントの1種で、矩形型のマスクを作ることができます。

    👉 【保存版】Unityのコンポーネント徹底解説【Unity基礎】

    矩形マスク機能に加え、ソフトマスク機能が追加実装されたのです。

    元画像(マスクなし)ソフトマスク後
    【Unity UI】uGUIできれいなソフトマスクを作る方法_1
    【Unity UI】uGUIできれいなソフトマスクを作る方法_1

    RectMask2Dコンポーネントを使うとこのようなソフトマスクが簡単に作れます。

    時間のない方は次の15秒ショート動画で確認してみてください。RectMask2Dの使い方を紹介しています。

    ここからは、文字でじっくりとRectMask2Dの使い方を解説していきます。

    👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

    ソフトマスク可能な「RectMask2D」の特徴4選

    最初にRectMask2Dコンポーネントの特徴を紹介します。

    RectMask2Dの特徴4選

    ①パフォーマンスの良いマスク

    ②マスク画像は不要

    ③マスク対象はRectMask2Dの子階層

    ④X・Y軸それぞれ具合の設定可

    特徴①パフォーマンスの良いマスク

    RectMask2Dとはマスクコンポーネントの1種です。特徴は マスク領域を四角形にしぼり、従来のMaskコンポーネントよりパフォーマンスを良い点

    そして、Unity2020からソフトマスクに対応しているということです。

    特徴②マスク画像は不要

    RectMask2Dにはマスク画像は不要です。なぜなら四角形に限定したマスク領域だからです。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_2

    マスクの範囲はRectTransformのWidth(幅)とHeight(高さ)です。

    特徴③マスク対象はRectMask2Dの子階層

    RectMask2DコンポーネントをアタッチしたGameObjectの子階層がマスク対象です。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_3

    子階層であれば複数のGameObjectもまとめてマスク可能です。

    特徴④X・Y軸それぞれ具合の設定可

    【Unity UI】uGUIできれいなソフトマスクを作る方法_4

    RectMask2Dコンポーネントの Softness の値を0以上にすると、x、y軸それぞれグラデーション具合を調整できます。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_5

    スクロールリストにソフトマスクを適用する4ステップ

    ソフトマスクの使い所の1つは スクロールの境界表現 です。

    今までUIデザイナーから 「スクロールの境界をグラデーションできれいに消したい」 という要望が多くありました。

    RectMask2Dを使うことで実現可能です。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_6

    スクロールリストにソフトマスクを適用する4ステップ

    ①ヒエラルキーに「Scroll View」を追加

    ②Viewportの「Mask」を削除

    ③「RectMask2D」をAddComponent

    ④ソフトマスク具合の調整

    上記の手順でソースコードを書かずにソフトマスク付きのスクロールリストを作れます。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_7

    ステップ①ヒエラルキーに「Scroll View」を追加

    まずはヒエラルキー上で右クリック。 UI > ScrollViewをクリックしましょう。

    ステップ②Viewportの「Mask」を削除

    作成されたScrollViewを調整していきます。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_8

    ViewportにはデフォルトでMaskコンポーネントがくっついています。この「Mask」を削除します

    【Unity UI】uGUIできれいなソフトマスクを作る方法_9

    ステップ③「RectMask2D」をAddComponent

    Maskを削除したGameObjectにRectMask2Dをアタッチします。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_10

    以上で準備は完了です。

    ステップ④ソフトマスク具合の調整

    最後に「Softness」ソフトマスクの消え方具合を調整します。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_11

    Softnessを調整することでマスクのグラデーション具合を調整可能です。

    ソフトマスク付きScrollViewの完成

    【Unity UI】uGUIできれいなソフトマスクを作る方法_12

    このようにソースコードを1行も書くことなく、ソフトマスク付きScroll Viewを作ることができるのです。

    Unityには難しいUI表現がいくつかあります。 「マスク表現」もその1つです。 Unity開発では重要なのは「簡単に実装することが難しいことを事前に知っておくこと」です。 次の記事ではUnityの難しい表現をまとめました。Unityエンジニアのほか、UIデザイナーもぜひ読んでUIデザインに生かしてみてください。

    RectMask2Dが使えない場合のソフトマスク2つの作り方

    RectMask2Dはあくまで四角形のマスクしか作れません。例えば 円形のマスクを作ることはできない のです。そこで2つの方法を紹介します。

    RectMask2Dが使えない場合のソフトマスク2つの作り方

    ①新たにUI用のマスクシェーダーの作成

    ②SoftMaskForUGUIの使用(おすすめ)

    標準機能のMaskコンポーネントで、きれいなソフトマスクを作れない

    結論から話すと標準機能の「Mask」ではきれいなソフトマスクは作れません。そもそもMaskコンポーネントがどういう仕様なのか確認しましょう。

    元画像マスク画像
    【Unity UI】uGUIできれいなソフトマスクを作る方法_13
    【Unity UI】uGUIできれいなソフトマスクを作る方法_13
    image.pngmask.png

    ↑このように2つの画像を用意します。

    理想現実
    【Unity UI】uGUIできれいなソフトマスクを作る方法_14
    【Unity UI】uGUIできれいなソフトマスクを作る方法_14

    理想と現実にはギャップあり。思ったのと違うものができあがります。

    MaskはON/OFF2択のみ

    Maskコンポーネントにはグラデーション表現はありません。白から黒にかけてグラデーション画像を用意したとしても、 グレーだからといって半透明表現になりません。

    つまり Maskコンポーネントでソフトマスクは表現できない のです。

    マスクの縁が汚い

    もう少し、Maskコンポーネントの特徴を紹介します。Maskコンポーネントの マスクの縁は汚い です。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_15

    ご覧の通りガタガタです。アンチエイリアスが効かない ため曲線を持つ図形ではジャギが目立ちます。

    マスクの輪郭を 上から別の画像で隠す といった工夫でしのぐことが多いです。別の対策として マスク画像の解像度を高くする ことでかなり軽減します。

    しかし、ソフトマスクの根本解決ではありません。

    解決方法①uGUI用のマスクシェーダーを自作する

    Maskコンポーネントを使わず、uGUIシェーダーを作成してソフトマスクを対応します。

    uGUIのシェーダーを作成する際はUnity公式からuGUIシェーダーをダウンロードしてカスタマイズします。Unity公式のシェーダーをコピーして最低限の変更を加えています。

    Unityダウンロードアーカイブを開きます。
    あなたが使っているUnityバージョンのシェーダー をダウンロードしましょう。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_16

    1. お使いのUnityバージョンの Downloads(Mac) ボタンをクリック
    2. Built in shaders をクリック

    ↑の動画の通りこの手順でシェーダーをダウンロードします。
    (執筆時はUnity2020.3.19f1を使用)

    Mac環境をベースに解説していますが、
    お使いのマシンがWindowsならWindowsのビルトインシェーダーをダウンロードしましょう。

    使っているUnityのバージョンがわからない方は、こちらの記事を参考にしてみてください。

    uGUIシェーダーのカスタマイズ

    Unity公式からuGUIシェーダーをダウンロードできたら早速カスタマイズしていきます。カスタマイズするシェーダーは UI-Default.shader です。格納場所はこちら DefaultResourcesExtra/UI/UI-Default.shader

    UI-Default.shaderをコピーして、プロジェクトに追加しておきましょう。「UI-SoftMask.shader」という名前にしました。

    今回の実装で重要なのはの2点です。

    1. Mask画像をセットできるようにする
    2. マスク画像のアルファ値を参照してカラー合成

    ステップ①Mask画像をセットできるようにシェーダーを修正

    Mask画像をインスペクタからセットできるようにシェーダーを調整します。
    Propertiesを調整 します。

    Properties  
    {
        // マスク画像を外からセットできるようにする  
        _MaskTex("MaskTexture", 2D) = "white"{}  
    

    Propertiesブロックに _MaskTex 変数を定義します。

    Pass  
    {
        // Mask画像を定義  
        sampler2D _MaskTex;  
    

    Passブロック中に Propertiesで定義した同名の _MaskTex 変数を定義します。

    マスク画像の用意

    今回のシェーダーで必要なソフトマスクを作る画像を用意します。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_17

    マスク画像自体ははグレースケール(白黒の濃淡画像)で作ります。白から黒に向かって透明になるのです。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_18

    TextureのインスペクタでAlpha from Grayscale にチェックを入れましょう。すると、黒に近づくにつれて透明(アルファ:0)に変換されます。

    ステップ②マスク画像のアルファ値を参照してカラー合成

    シェーダーの中で最も重要なのは次の2行です。

    // マスク画像のカラーを取得  
    half4 maskCol = tex2D(_MaskTex, IN.texcoord);  
    // マスク画像の透明度を出力のカラーにセット  
    color.a = maskCol.a;  
    

    フラグメントシェーダーで取得したマスク画像のピクセル色が大事なポイントです。

    ピクセルごとのアルファ値にマスク画像のアルファ値をセット します。つまり 各ピクセルの透明度はマスク画像の透明度になる のです。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_19

    このように きれいなソフトマスク を表現できました。


    uGUIシェーダーをカスタマイズすることは意外とあります。 基本機能では乗算しかできませんが、加算合成する場合はシェーダーをカスタマイズ します。次の記事ではUIの加算合成するシェーダーの作り方を紹介しています。本記事とあわせて読んでみてください。

    解決方法②外部アセット「SoftMaskForUGUI」できれいなマスクを作る方法

    標準機能ではありませんが「SoftMaskForUGUI」を使用することで、無料できれいなソフトマスクを作ることができます。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_20

    ↑このとおり、とてもきれいなソフトマスクを表現できます。

    SoftMaskForUGUIのインストール方法

    SoftMaskForUGUIはパッケージで提供されているため、Package Managerでインストールするのがおすすめです。 メニューWindow > Package Manager からPackage Managerウィンドウを開きます。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_21

    ①の「+」ボタンをクリックします。②にURL https://github.com/mob-sakai/SoftMaskForUGUI を入力して、③のAddボタンをクリックしてください。

    するとインストールは完了します。

    SoftMaskForUGUIの使い方

    では具体的なSoftMaskForUGUIの使い方を解説します。まずはマスクする側を親、マスクされる側を子という関係のGameObjectを作ります。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_22

    ↑上図のようにMaskがマスクする側、Imageがマスクされる側です。

    マスクする側に「SoftMask」「Image」という2つのコンポーネントをセットしましょう。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_23

    ↑上図のように2つのコンポーネントを追加します。セットする順は自由です。Imageコンポーネントにセットする画像がマスク画像になります。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_24

    今回使用するマスク画像は↑上の円形グラデーションにしました。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_25

    TextureTypeを「Sprite(2D and UI)」に設定します。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_26

    Imageの「Source Image」に設定してみましょう。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_27

    すると↑上図のとおりきれいなソフトマスクが作れます。

    SoftMaskForUGUIは多階層に対応

    SoftMaskForUGUIは「マスクされる側」の階層に依存せずマスクをかけられます。つまり子階層が深かかろうときれいなソフトマスクを作れるのです。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_28

    上図のようにマスクされる側の階層を3階層するとどうなるでしょうか。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_29

    SoftMaskForUGUIは子階層の階層数に関係なく、きれいにソフトマスクを作ることができるのです。

    SoftMaskForUGUIはMaskの子階層だけどマスク対象から除外できる

    SoftMaskForUGUIの便利な機能を紹介します。通常Maskコンポーネントの子階層は強制的にマスクされてしまいます。しかし、SoftMaskForUGUIはMaskの子階層のオブジェクトをマスク対象から外すことができる のです(下図参照)。非常に便利です。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_30

    設定方法は簡単です。マスクされる側に自動的にセットされる「SoftMaskable」の設定を変更します。

    【Unity UI】uGUIできれいなソフトマスクを作る方法_31

    【Unity UI】uGUIできれいなソフトマスクを作る方法_32

    「Mask Interaction」を「Custom」、「Mask 0」を「None」に設定しましょう。するとマスク対象から除外できます。

    とても便利で使いやすいSoftMaskForUGUIですが、処理を見る限りMaskコンポーネントより負荷は上がります。使用する際は負荷計測しつつ使ってみてください。

    → SoftMaskForUGUIの公式サイトはこちら

    Unity UIでソフトマスクを作る方法まとめ

    記事前半ではUnity2020から実装された RectMask2Dのソフトマスク を紹介しました。

    簡単にソフトマスクを作れるRectMask2D

    ・RectMask2Dの子階層がマスク対象

    ・SoftnessでX、Y軸に対して設定化

    ・要望の多かったソフトマスク付きスクロールがデフォルトで実装可能

    ・プログラミングはいっさいなし

    また、記事後半ではRectMask2Dの使えない状況におけるソフトマスクの実装方法を紹介しました。

    RectMask2Dを使わないソフトマスク

    ・uGUIシェーダーをカスタマイズして自作

    ・SoftMaskForUGUI(無料)を使う

    Unityの進化によって基本機能でできることが増えています。ありがたいことです。

    しかし、 まだまだできないことは多い です。ソフトマスクはまだ発展途上。RectMask2Dは矩形のみに対応しました。RectMask2Dを使わない場合は、カスタムシェーダーの自作、または外部ライブラリである「SoftMaskForUGUI」を使う方法を紹介しました。いずれ標準機能でソフトマスクが実現できる日が来るかもしれません。

    今後のUnityの進化に期待し、 新機能には敏感にウォッチ しておきたいですね。

    とりあえず、今Unityのソフトマスクで悩んでいる方がこの記事を読んで課題を解決できていると嬉しいです。もしこの記事を読んでいるのがデザイナーさんだった場合、ぜひUI実装担当のエンジニアに見せてあげてください。なにか実装のヒントになるかもしません。

    この記事があなたのゲーム開発に少しでもお役に立てたら嬉しいです。

    今回使ったソースコード公開

    今回作成したシェーダーコード全文はこちらです。
    ご自由にお使いください。

    // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)  
    Shader "UI/SoftMask"  
    {
        Properties  
        {
            [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}  
            _Color ("Tint", Color) = (1,1,1,1)  
    
            _StencilComp ("Stencil Comparison", Float) = 8  
            _Stencil ("Stencil ID", Float) = 0  
            _StencilOp ("Stencil Operation", Float) = 0  
            _StencilWriteMask ("Stencil Write Mask", Float) = 255  
            _StencilReadMask ("Stencil Read Mask", Float) = 255  
    
            _ColorMask ("Color Mask", Float) = 15  
    
            [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0  
    
            _MaskTex("MaskTexture",2D) = "white"{}  
        }
    
        SubShader  
        {
            Tags  
            {
                "Queue"="Transparent"  
                "IgnoreProjector"="True"  
                "RenderType"="Transparent"  
                "PreviewType"="Plane"  
                "CanUseSpriteAtlas"="True"  
            }
    
            Stencil  
            {
                Ref [_Stencil]  
                Comp [_StencilComp]  
                Pass [_StencilOp]  
                ReadMask [_StencilReadMask]  
                WriteMask [_StencilWriteMask]  
            }
    
            Cull Off  
            Lighting Off  
            ZWrite Off  
            ZTest [unity_GUIZTestMode]  
            Blend One OneMinusSrcAlpha  
            ColorMask [_ColorMask]  
    
            Pass  
            {
                Name "Default"  
            CGPROGRAM  
                #pragma vertex vert  
                #pragma fragment frag  
                #pragma target 2.0  
    
                #include "UnityCG.cginc"  
                #include "UnityUI.cginc"  
    
                #pragma multi_compile_local _ UNITY_UI_CLIP_RECT  
                #pragma multi_compile_local _ UNITY_UI_ALPHACLIP  
    
                struct appdata_t  
                {
                    float4 vertex   : POSITION;  
                    float4 color    : COLOR;  
                    float2 texcoord : TEXCOORD0;  
                    UNITY_VERTEX_INPUT_INSTANCE_ID  
                };  
    
                struct v2f  
                {
                    float4 vertex   : SV_POSITION;  
                    fixed4 color    : COLOR;  
                    float2 texcoord  : TEXCOORD0;  
                    float4 worldPosition : TEXCOORD1;  
                    half4  mask : TEXCOORD2;  
                    UNITY_VERTEX_OUTPUT_STEREO  
                };  
    
                sampler2D _MainTex;  
                fixed4 _Color;  
                fixed4 _TextureSampleAdd;  
                float4 _ClipRect;  
                float4 _MainTex_ST;  
                float _UIMaskSoftnessX;  
                float _UIMaskSoftnessY;  
                sampler2D _MaskTex;  
    
                v2f vert(appdata_t v)  
                {
                    v2f OUT;  
                    UNITY_SETUP_INSTANCE_ID(v);  
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);  
                    float4 vPosition = UnityObjectToClipPos(v.vertex);  
                    OUT.worldPosition = v.vertex;  
                    OUT.vertex = vPosition;  
    
                    float2 pixelSize = vPosition.w;  
                    pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));  
    
                    float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);  
                    float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);  
                    OUT.texcoord = TRANSFORM_TEX(v.texcoord.xy, _MainTex);  
                    OUT.mask = half4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));  
    
                    OUT.color = v.color * _Color;  
                    return OUT;  
                }
    
                fixed4 frag(v2f IN) : SV_Target  
                {
                    half4 color = IN.color * (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd);  
                    half4 maskCol = tex2D(_MaskTex, IN.texcoord);  
                    color.a = maskCol.a;  
    
                    #ifdef UNITY_UI_CLIP_RECT  
                    half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);  
                    color.a *= m.x * m.y;  
                    #endif  
    
                    #ifdef UNITY_UI_ALPHACLIP  
                    clip (color.a - 0.001);  
                    #endif  
    
                    color.rgb *= color.a;  
    
                    return color;  
                }
            ENDCG  
            }
        }
    }
    

    このシェーダーをUnityプロジェクトにコピー後、Materialにアタッチしてお使いください。

    「Unity初心者大学」というUnity初心者向けのYouTube始めました!!
    ぜひチャンネル登録をお願いします!

    最後まで読んでいただきありがとうございました!
    すばらしいソフトマスクライフをお過ごしください。

    オススメ記事
    検証環境