/unity-alphamask

Unity でアルファマスクを使う

Primary LanguageC#

Unity でアルファマスクを使う

現在、モバイルプラットフォームにおいてアルファ付きのテクスチャ画像を扱うことは、比較的困難なハードルとなっています。次のような問題点があるためです。

  • iOS の場合:アルファ付きの PVRTC は非常に画質が低い。
  • Android の場合:ETC ではそもそもアルファが使えない。

いくつかの改善方法が考えられる(例1例2)ものの、プラットフォームによって異なる手法を用いることは、煩雑さを生み出す要因となりかねません。

ここでは、この問題を解決する手法として、Unity でマスクを扱う方法について解説します。マスクを用いることによりアルファチャンネルが不要になるため、上記の問題を一括して解決できる可能性があります。

マスクの設定

下図のように、色成分を含むベース画像と、アルファのみを含むマスク画像を、個別のテクスチャに格納します。

Textures

このとき、マスクはグレイスケールで不透明度を表すようにします。

そして、それぞれのテクスチャをアルファ無しの圧縮形式に変換します。具体的には、iOS であれば PVRTC RGB 4bit を、Android であれば ETC RGB 4 bit を用います。

描画時にはマスクの設定ができる特殊なシェーダーを用います。

Inspector

これにより、1 ピクセルあたり 8 bit のコストで、比較的良好な画質での描画が実現できます。

エディタスクリプトの実装例

この手法は、ベースとマスクを別々のファイルに用意し、手動でインポートしていくことによっても実現が可能です。

エディタスクリプトを使えば自動化も可能です。ここでは例として、png ファイルからアルファチャンネルを分離して自動的にマスクを生成するスクリプトを組んでみました。

Editor/TextureModifier.cs

このスクリプトでは、ファイル名の末尾が “with alpha.png” の場合に、そのファイルからアルファチャンネルを抜き出してマスク用テクスチャを生成します。そして各々のテクスチャを適切な圧縮フォーマットで再変換します。

このようなスクリプトによりある程度の自動化が可能ですが、以下のようなデメリットもあります。

  • プラットフォーム切り替え時に再インポート (Reimport) を手動で発動しなければならない。
  • プラットフォーム切り替え時にアセットが更新される(バージョン管理に影響を与える可能性がある)。

ワークフローによっては、手動でも十分な作業効率を得られる場合があります。ケースバイケースで自動化の検討を行うのが望ましいです。

シェーダーの実装例

Unity の標準シェーダーには「マスクの設定が可能なシェーダー」が存在しないため、これを自前で実装する必要があります。以下はその一例です。

SpriteWithMask.shader

このシェーダーではマスクの他に色 (Color) が設定可能になっています。(127,127,127,127) で元の色がそのまま出力され、数値の増減により明暗両方への調整が可能です。

テスト結果

iOS の場合

左はベース、右はマスク。それぞれ PVRTC RGB 4bit で圧縮済みです。

iOS Base Texture![iOS Mask Texture](https://github.com/keijiro/unity-alphamask/raw/gh-pages/PVRTC Mask.png)

合成して表示すると以下のようになりました。

![iOS Result](https://github.com/keijiro/unity-alphamask/raw/gh-pages/PVRTC Result.png)

Android の場合

左はベース、右はマスク。それぞれ ETC RGB 4bit で圧縮済みです。

Android Base Texture![Android Mask Texture](https://github.com/keijiro/unity-alphamask/raw/gh-pages/ETC Mask.png)

合成して表示すると以下のようになりました。

![Android Result](https://github.com/keijiro/unity-alphamask/raw/gh-pages/ETC Result.png)

考察

アルファ値を持つためだけに 4 bit/pixel を追加で捻出するのはナンセンスにも思えますが、現状において 4 bit/pixel 未満のコストでグレイスケールを格納できる形式は存在しないため、しょうがないとも言えます。16 bit RGBA4444 に 4 枚分のマスクを織り込んだとしても 4 bit/pixel は変わりません。また、モバイル GPU の性能ではビット単位でアルファを織り込むということもできません(シェーダー内で整数ビット演算ができないため)。

ここで解説したような、RGB の圧縮形式でグレースケールのマスクを持たせるという手法は、プラットフォームを跨いで運用可能な、もっとも無難な解決法であると言えるのではないかと思います。

謝辞(テスト画像について)

上でテストに用いている画像 (Test A, Test B) はテラシュールウェアさんよりご提供いただいたものです。 この画像を検証以外に用いることは避けてください。

スクリプトの使用について

このプロジェクトに含まれるスクリプト (TextureModifier.cs) およびシェーダー (SpriteWithMask.shader) は商用・非商用を問わず自由に利用して構いません。