WebGLを本格的に使っていると、テクスチャのメモリオーバーは一度や二度直面したこともあるはずです。WebGL関連のドキュメントを追っていて知っている人もいるかと思いますが、Webエンジニアには意外と知られていないCompressedTextureというものがあります。

参考: Compressed texture formats

PNGやJPEGなどはテクスチャとしてGPUに転送される前に24bpp-32bpp(bit/pixel)に展開されるため、わずか30-40KBの1Kのテクスチャが突如4MBに、4Kのテクスチャが67MBに膨れ上がるという大災害が発生してしまいます。一方、CompressedTextureは展開せずにGPUに送ることができ、固定の圧縮率ながら非圧縮に比べて 1/8 〜 1/4 程度圧縮できるため、利用できるメモリ空間が飛躍的に広がります。

モバイルブラウザの制約を考えると1GB程度には収めたいところですから、使わない手はありません。

どの形式を使うのか

PNG/JPEGの場合は使い分けのノウハウが一般的に知られているためあまり迷うことはありませんが、最初に心が折れかける問題はさまざまなフォーマットがあり、ブラウザによって使用できる種類が異なるということです。まず、WebGLで使える形式は以下の通りです。

WebGLで使えるCompressedTextureの形式

名前 特徴
BC1, BC3 DirectXでよく見かけたフォーマット。BC1は4bpp/2値アルファ。BC3は8bpc/多階調アルファ。写実的で情報量が多いとそれなりに見えるけど、グラデーションや輪郭がはっきりしたものは品質が悪い。
PVRTC PowerVR系GPUで使われる。iOSでは常に使えたので昔は使われていた印象。輪郭がはっきりした画像には向いていない。
ETC Androidで常に使えたので昔は使われていた印象。4bpp。輪郭がはっきりした画像に向いていない。
ASTC 新しめのモバイル端末では大体使える。macOSでも利用できて高品質。圧縮率は可変的。4bpp-8bpp程度が使いやすい
BC7 BC系の新しめのフォーマット。BPTCとも。ASTCと似たような高品質。Windows端末や、Safari16から対応

といった感じ。個人的には、ASTC/BC7をメインにして、それ以外のものはフォールバックするか、元画像を縮小して提供すると良いのではないかと思う。

どの端末・ブラウザに、何を使うのか

種類がわかったところで、問題はブラウザごとに対応が異なる点。調べた限りでのおおむねの対応表を作りましたが、鵜呑みにせず、最新の情報は各端末で WebGL Report をみて決めてください。

Type Chrome(win) Chrome(mac) Safari iOS Android
BC1/BC3
PVRTC
ETC
ASTC
BC7

ということで、おすすめの構成は以下です。

  • iOS
    • ASTC。どうしても古い端末に対応したい場合はPVRTC。
  • Android
    • ASTC。古い端末に対応したいならETC。
  • Windows
    • BC7。古い端末に対応したいならBC1/3
  • macOS
    • Safari: ASTC
    • Chrome: 非圧縮 or BC1/3

どの形で配信するのか

CompressedTextureはデータの圧縮方法であり、ファイルコンテナはいくつか選択肢があります。mp4における、h.264やh.265のような関係と同じです。個人的には全てのフォーマットに対応できて、WebではスタンダードになりつつあるKTX2形式がおすすめです。

three.jsには KTX2Loader がありますし、テクスチャを取り出すだけであれば意外と難しくないので、 KTX File Format を読んで自分でパースしてしまっても良いかもしれません。

何でエンコードするのか

導入するぞと思って一番困るのはこれです。Photoshopでは書き出せませんし、sharpでも対応してませんし、有望なWebサービスはありません。エンコード時間が非常にかかるためLambdaなどによる即時的な変換には向いていないフォーマットです。

最近有望そうなのはこの辺りでしょうか? BinomalLLC/basis_universal
ktx2形式でパックできますし、幅広いフォーマットに対応しているスーパープロジェクトです。

開発時には Imagination の PVRTexTool の利用がお勧めです。ビューアがあり、GUI操作で出力もできます。ちなみにこちらのツールもCLI版があり、BC7以外はこのバイナリでもエンコード可能です。

まとめ

ここで紹介した以外に、ランタイムで利用できるテクスチャの判定、利用可能なテクスチャの管理、配置、アクセスなどの下準備が必要でハードルは高いかと思います。しかしながらメリットも大きいので、WebGLを使うならCompressedTextureをしっかり活用して、帯域とメモリに優しくなりましょう。