インスタで小さい子供やペットの可愛い写真や動画を載せていたアカウントが急に商品の宣伝をしだすとなんだか悲しくなるナイーブな皆さんこんにちは。子供やペットが無邪気な分、将来の姿を勝手に心配してしまいます。
UIのマスク
UnityではImageなどのUIに関してはMask機能が用意されていて、切り抜きたい形の画像にAddComponentでMaskコンポーネントをアタッチして、その子オブジェに切り抜かれる側の画像を配置すれば良いだけとなっています。
なので非常に手軽に実装できるのですが、Imageの内側をくり抜くような逆マスクには対応していないので、そういった場合には有志の方が公開してるアセットを使う必要がありました。
そういった状況でしたが、Unity公式コミュニティの投稿によりMaskコンポーネントもオブジェクトの親子関係も必要とせずにマスク処理、及び逆マスク処理を行う手法が明らかになったようです。
マスク処理(逆マスク含む)と言えばシェーダーのステンシル処理を使う為、これまではシェーダーに手を加える必要がありましたが、上記の手法ではInspetor上からステンシル用の設定を行う事ができる為、新たにシェーダーを起こす必要はありません。
今回はその手法を利用して、いままでは行う事ができなかったTextMeshProUGUIの文字列でのUIのマスクを試みてみようと思います。
TextMeshProUGUI側の設定
今回はTextMeshProUGUIの文字列でマスクするため、TextMeshProUGUIがステンシルバッファにRef値を書き込む必要があります。
最初にTextMeshProUGUI用のMaterialを用意する為、TextMeshProUGUIのInspetorにあるSDFのアイコンを右クリック→「Create Material Preset」でMaterialを作成します。
その際、分かりやすいような名前を設定しておくと後で使いまわし易くなります。
作成したMaterialのシェーダーにはステンシル処理用の設定を行う為、「UI→Default」を指定します。(後でTextMeshProUGUI用のシェーダーに変えます)
「UI→Default」では以下のように設定します。
重要なのはStencil ID(Ref値)とStencil Comparison(比較方法)とStencil Operation(操作方法)なんですが、どれも数値で指定する必要があるため、一度マニュアルで確認してください。
ステンシルの設定を行ったら、シェーダーをTextMeshProUGUI用のシェーダーに変更します。シェーダーに「TextMeshPro -> Mobile -> Distance Field – Masking」を指定、先程のステンシルの設定が内部に保存されたままTextMeshProUGUI用のシェーダーに切り替わります。(Inspector上からは見えなくなります)
Image側の設定
マスクされる側のImageは何でも良いのですが、マスク効果が分かりやすいように今回は白い四角を使いました。
マスクされる(文字列で切り抜かれる)Imageの方もステンシル処理用の設定を行うのでシェーダーに「UI→Default」を指定し、以下の設定を行います。
ここでStencil IDには先程TextMeshProUGUI側で指定した値と同じ値を設定、Stencil Comparisonに「Equal」(レファレンス値がバッファの値と等しい場合のみレンダリング)を指定すると
Image(白い四角)が文字列の形で切り抜かれました。
今度はStencil Comparisonに「NotEqual」(レファレンス値がバッファの値と等しくない場合にレンダリング)を指定します。
以下のように逆マスクとして機能します。
注意点
先程も記述しましたが、TextMeshProUGUI側では一度設定するとStencil ID(Ref値)が見えなくなるので、設定時にはメモ等の記録を取っておくことをお勧めします。
また、当然ながらマスク側(TextMeshProUGUI)はマスクされる側(Image)より先に描画する必要があるので、ヒエラルキー上の順番は「マスクされる側」が「マスク側」の下の順になるように配置してください。(オブジェクトを親子関係とする事は不要です)