WebXRでオススメのKTX2とは
旧Facebook(現Meta)社が WebXRにおいてKTX2 textureをオススメしている件についてはコチラ↓で触れました。
改めてKTX2について調べてみたいと思います。
Khronos TeXture、略してKTXです。
ハッシュタグに KTXがあったので設定したら韓国の電車?が出てきました…
gltfで利用できる軽量で高品質なテクスチャ
2021年から使えるようになったらしいです。
それまでのテクスチャ技術をブッちぎる…らしいので、初めて知る方はここからスタートしても良いかもしれません。(昔ながらの技術を御存じの方々はしみじみするかも知れません)
古い Three.jsのバージョンなどを使っている場合は対応していないかも知れません。A-Frameも要注意です。
圧縮した状態でGPUが扱える
例えば動画データなどをzip圧縮した場合、見たいと思ったら"解凍"する必要があります。テクスチャとしてJPGや高品質PNGなどを使った場合も同じです。圧縮し、使うときは解凍します。
このKTX 2.0は、圧縮した状態のデータを直接GPUが扱えるという特徴があるので解凍する必要がなく、メモリー利用が非常に少なくて済むという特徴があります。ユーザーにとっては最終的にレンダリングされたものが見えれば良いのですから、メモリー利用が少なくて済む方を使いたいですよね。
※「GPU用の圧縮テクスチャなんて昔からあったよ!最近の若いもんは~」という方もいらっしゃると思うのですが、一旦置いておきます
以下のインテリアのサンプルでは、左側でメモリーを100MB近く食うのに対し、右側のKTXフォーマットであれば20MB程度で済むということになっています。品質に差がないままに、メモリー利用が1/5で済むというのは魅力的です。
使い方
Babylon.jsであれば、KTX2のgltfファイルをそのまま使えます。
扱えるファイル形式はThree.jsの方が多いのですが、特別に何かしなくてよいというのは楽です。
作り方
Khronosグループが提供しているツールである totkx exe や、gltfpackを使うことが出来ます。
gltfpackを使ってみます。
オプションに -tc を付けることで、KTX2 に変換することが出来ます。
$ gltfpack -h
Textures:
-tc: convert all textures to KTX2 with BasisU supercompression (using basisu/toktx executable)
$ gltfpack -tc -i note_sample_input.glb -o note_sample_output.glb
gltf-transformを使って確認します。ちなみに、元のglbファイルはBlenderで出力したものです。
ファイルサイズもかなり小さくなっていたのですが、下記の注意点②にあるように適切な調整が必要であるため、あくまで変換できたという所の確認だけ貼っておきます。
// 変換前
$ gltf-transform inspect note_sample_input.glb
METADATA
────────────────────────────────────────────
┌────────────────────┬──────────────────────────────────┐
│ key │ value │
├────────────────────┼──────────────────────────────────┤
│ version │ 2.0 │
├────────────────────┼──────────────────────────────────┤
│ generator │ Khronos glTF Blender I/O v1.5.17 │
├────────────────────┼──────────────────────────────────┤
│ extensionsUsed │ none │
├────────────────────┼──────────────────────────────────┤
│ extensionsRequired │ none │
└────────────────────┴──────────────────────────────────┘
MESHES
────────────────────────────────────────────
┌──────┬───────────────┬───────────┬────────────┬──────────────┬──────────┬─────────┬──────────────────────────────────┬───────────┬────────────┐
│ # │ name │ mode │ primitives │ glPrimitives │ vertices │ indices │ attributes │ instances │ size¹ │
├──────┼───────────────┼───────────┼────────────┼──────────────┼──────────┼─────────┼──────────────────────────────────┼───────────┼────────────┤
│ 0 │ メッシュ │ TRIANGLES │ 1 │ 12 │ 8 │ uint16 │ NORMAL:float32, POSITION:float32 │ 1 │ 264 Bytes │
├──────┼───────────────┼───────────┼────────────┼──────────────┼──────────┼─────────┼──────────────────────────────────┼───────────┼────────────┤
│ 1 │ メッシュ.001 │ TRIANGLES │ 1 │ 12 │ 8 │ uint16 │ NORMAL:float32, POSITION:float32 │ 1 │ 264 Bytes │
├──────┼───────────────┼───────────┼────────────┼──────────────┼──────────┼─────────┼──────────────────────────────────┼───────────┼────────────┤
│ 2 │ メッシュ.002 │ TRIANGLES │ 1 │ 12 │ 8 │ uint16 │ NORMAL:float32, POSITION:float32 │ 1 │ 264 Bytes │
├──────┼───────────────┼───────────┼────────────┼──────────────┼──────────┼─────────┼──────────────────────────────────┼───────────┼────────────┤
│ 3 │ メッシュ.003 │ TRIANGLES │ 1 │ 28 │ 16 │ uint16 │ NORMAL:float32, POSITION:float32 │ 1 │ 552 Bytes │
├──────┼───────────────┼───────────┼────────────┼──────────────┼──────────┼─────────┼──────────────────────────────────┼───────────┼────────────┤
│ 4 │ メッシュ.004 │ TRIANGLES │ 1 │ 20 │ 12 │ uint16 │ NORMAL:float32, POSITION:float32 │ 1 │ 408 Bytes │
~~以下延々とメッシュの情報が続く~~
//変換後
$ gltf-transform inspect note_sample_output.glb
METADATA
────────────────────────────────────────────
┌────────────────────┬───────────────────────┐
│ key │ value │
├────────────────────┼───────────────────────┤
│ version │ 2.0 │
├────────────────────┼───────────────────────┤
│ generator │ gltfpack 0.16 │
├────────────────────┼───────────────────────┤
│ extensionsUsed │ KHR_mesh_quantization │
├────────────────────┼───────────────────────┤
│ extensionsRequired │ KHR_mesh_quantization │
└────────────────────┴───────────────────────┘
MESHES
────────────────────────────────────────────
┌───┬──────┬───────────┬────────────┬──────────────┬──────────┬─────────┬──────────────────────────────┬───────────┬───────────┐
│ # │ name │ mode │ primitives │ glPrimitives │ vertices │ indices │ attributes │ instances │ size¹ │
├───┼──────┼───────────┼────────────┼──────────────┼──────────┼─────────┼──────────────────────────────┼───────────┼───────────┤
│ 0 │ │ TRIANGLES │ 1 │ 77,672 │ 46,942 │ uint16 │ NORMAL:int8, POSITION:uint16 │ 1 │ 867.69 KB │
└───┴──────┴───────────┴────────────┴──────────────┴──────────┴─────────┴──────────────────────────────┴───────────┴───────────┘
Blender以降はパイプラインを構築して自働化せよというのがMeta社のメッセージにあったので、一連の流れはスクリプト化してしまうのが良いと思います。
注意点①:Windowsの3Dビューワでは見れないのでBabylon.jsのサイトなどで確認
Windowsの3Dビューワは手軽ですが、ガリガリ圧縮した後では「ファイルが壊れている」と出てしまうので、もし簡単にデータを見たい場合は以下のサイトで確認してください。
Three.js作者のサイト
Babylon.jsの公式ファイルビューワー
注意点②:用途別の設定は必要
大元となる技術は"Basis Universal"圧縮方式なのですが、これにはETC1S と UASTC の 2つのモードがあります。
ETC1Sはシンプルなテクスチャ向けで、単色でシンプルな構成な時に高いパフォーマンスを発揮します。WebXR内のボタンなどのUIパーツなどで使うと良い、とのことです。むしろ法線マップなどでは上手くいかないとのこと。
UASTC は反対に詳細なカラー設定がされているようなテクスチャで効果を発揮するとのこと。gltfpackでは UASTCを利用するオプションがあり、変換時に -tu を設定することで利用できます。
$ gltfpack -h
Textures:
-tc: convert all textures to KTX2 with BasisU supercompression (using basisu/toktx executable)
-tu: use UASTC when encoding textures (much higher quality and much larger size)
-tq N: set texture encoding quality (default: 8; N should be between 1 and 10
-ts R: scale texture dimensions by the ratio R (default: 1; R should be between 0 and 1)
-tp: resize textures to nearest power of 2 to conform to WebGL1 restrictions
「なんか分からないけどKTX2に変換すればよい」というわけではなく、
最適化されるどころか使い勝手が悪いデータに変換されてしまう場合があるので注意が必要です。
おわりに
「Basis Universal 圧縮技術にKTX2.0フォーマットが対応、
更に gltfがKTX2.0に対応」という流れなのですが、元のBasisU技術というものによりKTX2.0を使っているgltfはターゲットデバイス(iOS/Androidなど)を気にしなくても良いようになりました。
※過去の技術ではターゲットがiOSかAndroidかなど気にする必要があった
弊社では、用途に応じて処理を変えたり、ある対応をするなどしています。
3Dアセット制作、Webサイト制作ならお任せ下さい
お手持ちの商材の3D化やメタバース技術を活用したプロモーションなど、お気軽にご相談いただければと思います。
Googleフォーム: https://forms.gle/CLeoLxkskQRXnaLKA