KTX2・DRACOによる圧縮・軽量化技術について

Three.js

WebGL・three.jsなどで 画像、モデルを圧縮して表示する方法の備忘録です。
古くから採用されているようで、簡単に使うことができますが、あまり知られていないようなので、自分用に、簡単にまとめてみました。

画像を圧縮する

画像を単純に圧縮するのなら、JPEG・PNGを圧縮してもらえるTinypngWebp形式で保存することが有名ですが、Three.jsやbabylon.jsで画像でテクスチャを使うのあれば、KTX2形式で保存することが有効に働くことがあります。最大の特徴は、圧縮した状態のままGPUに画像テクスチャを持っていけることにあり、JPEG・PNGと比べてGPUメモリを抑えることができます。※こちらを参照

TinyPNG – Compress AVIF, WebP, PNG and JPEG images
Free online image optimizer for faster websites! Reduce the file size of your AVIF, WEBP, JPEG and PNG images while pres...
Squoosh
Squoosh is the ultimate image optimizer that allows you to compress and compare images with different codecs in your bro...

KTX2を使うには、KTX-Softwareから専用のソフトを使い、ターミナル経由でローカルで実行することが、基本ステップになります。(変換できる専用サイトもありますが、ここでは省略させていただきます)

※ただし、3Dモデルのみの使用で、画像単体を変換しないのであればソフトのダウンロード不要です。glTF Transformを使えばインストール不要でKTX2化できます。

Windowsであれば、KTX-Software-4.4.2-Windows-x64.exe をダウンロードしてインストールいたします。DLしたexeを実行して、そのままクリックを進めてインストールが完了できると思います。

GitHub - KhronosGroup/KTX-Software: KTX (Khronos Texture) Library and Tools
KTX (Khronos Texture) Library and Tools. Contribute to KhronosGroup/KTX-Software development by creating an account on G...

インストール完了後、VScode でフォルダに移動して、以下のコマンドを実行することで、対象の画像を選択して、KTX2に変化します。

以下にコマンドは、Viteで npm create vite@latest 開発サーバーを作成して上で、publicフォルダにimageフォルダを作成して、sakura.jpgを入れた画像を使う例です。

toktx --t2 --encode etc1s --clevel 5 --qlevel 128 "public/image/sakura.ktx2" "public/image/sakura.jpg"

今回は、illust-ACさんより、Sakuraの画像をお借りして変換させてみました。

元画像(9.2MB 変換後→ KTX2(1.84MB) ほぼ見た目は変わりなく、80%削減できました。また、複数の画像を読み込んで比較したとき、パフォーマンスを見れるStatsでのmemoryの値は上昇するのですが、FPS の安定性はよくなります。このmemoryの値は上昇は調査中です。

import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
// import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js'


~基本構文は省略~


// KTX2テクスチャを複数読み込んでPlaneに表示
const texturePaths = [
  '/image/sakura.ktx2',
  // '/image/school.ktx2',
]

// 画像表示用のplane
const planeGeometry = new THREE.PlaneGeometry(3.2, 2.2)
const xStart = 0
const xGap = 1.0

// テクスチャの読み込み
texturePaths.forEach((path, index) => {
  ktx2Loader.load(path, (texture) => {
    texture.colorSpace = THREE.SRGBColorSpace
    texture.needsUpdate = true

    const material = new THREE.MeshStandardMaterial({ map: texture,side: THREE.DoubleSide })
    const plane = new THREE.Mesh(planeGeometry, material)
    plane.position.set(0, 0, xStart + xGap * index)
    plane.rotation.set(Math.PI,0,0);

    scene.add(plane)
    console.log(`Texture loaded: ${path}`)
  }, undefined, (error) => {
    console.error(`Error loading texture: ${path}`, error)
  })
})
Basis Universal GPU Texture Compression
JavaScript 3D Library.

ここまでが、基本的なクイックスタートになります。

toktx --t2 --encode etc1s --clevel 5 --qlevel 128 "public/image/sakura.ktx2" "public/image/sakura.jpg"

KTX-Softwareをインストール後に、ターミナルで使えるようになるコマンドが toktx になります。
–t2KTX2使用するというコマンドでBasis Universal対応なので、–t2が推奨されているようです。
ここで、重要になるのが、次の etc1s になります。etc1sの代わりに、uastcというものに変更することができます。

toktx --t2 --encode uastc "public/image/sakura.ktx2" "public/image/sakura.jpg"

uastc では、GPUのメモリ消費がETC1Sより大きくなりますが、画像品質が上がるメリットがあります。しかし、私が使用した限り、変換失敗して0MBになることがあり、実質うまくいかないことが多かったイメージです。
もっと詳しく知りたい方は、コマンドとともにぜひ調べてみてください

KTX2 フォーマット | metatell documentation
glbファイル内のテクスチャをKTX2フォーマットに変換することで、GPUメモリの使用量を削減し、3Dモデルの読み込み速度を向上させることができます。
KTX Tools Reference: toktx

3Dモデルの圧縮(.glb)

three.js上に3Dモデルを表示させるのに、今回はblenderからモデルをglb形式で書き出して表示してみたいと思います。

Blenderで書き出すときに、以下のように圧縮を選択して、glbを書き出しましょう。(MyModel.glb)

このモデルをthree.jsにてDLして表示されるコードです。

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js'

~基本構文は省略~

// KTX2Loaderのセットアップ
const ktx2Loader = new KTX2Loader()
ktx2Loader.setTranscoderPath('https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/libs/basis/')
ktx2Loader.detectSupport(renderer)

// DRACOLoaderのセットアップ
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.7/')

// GLTFLoaderのセットアップ
const gltfLoader = new GLTFLoader()
gltfLoader.setDRACOLoader(dracoLoader)
gltfLoader.setKTX2Loader(ktx2Loader)

// モデルの読み込み
gltfLoader.load(
  '/model/MyModel.glb',
  (gltf:any) => {
    console.log('Model loaded successfully!')
    const model = gltf.scene
    
    // モデルのサイズを調整
    const box = new THREE.Box3().setFromObject(model)
    const center = box.getCenter(new THREE.Vector3())
    const size = box.getSize(new THREE.Vector3())
    const maxDim = Math.max(size.x, size.y, size.z)
    const scale = 2 / maxDim
    model.scale.multiplyScalar(scale)
    
    // モデルを中央に配置
    const adjustedCenter = box.getCenter(new THREE.Vector3()).multiplyScalar(scale)
    model.position.sub(adjustedCenter)
    
    scene.add(model)
  },
  (progress:any) => {
    const percent = (progress.loaded / progress.total) * 100
    console.log(`Loading: ${percent.toFixed(2)}%`)
  },
  (error:any) => {
    console.error('Error loading model:', error)
  }
)

これでモデルが表示されると思います。ここで重要なのが DRACOLoader になります。DRACOは、3D ジオメトリ メッシュとポイントを圧縮できるオープンソースのプログラムで、blenderでglbで圧縮した場合、DRACOを使い解凍させる感じになります。

しかし、blenderの圧縮では、テクスチャは圧縮されていないので、テクスチャをKTX2化させてさらにサイズを小さくしていきましょう。

ここで新しく、glTF Transformを便利なツールを使っていきます。使える状態にするためにインストールしてください。

npm install --save @gltf-transform/core @gltf-transform/extensions @gltf-transform/functions
glTF Transform
glTF 2.0 SDK for JavaScript and TypeScript, on Web and Node.js.
npx gltf-transform etc1s public/model/FLOWERS_.glb public/model/FLOWERS_etc1s.glb --verbose

npx gltf-transform draco public/model/FLOWERS_etc1s.glb public/model/FLOWERS_etc1s_draco.glb --verbose

一度、dracoで圧縮されているものを展開して、画像をKTX2k化させて、再度、Dracoでメッシュを圧縮させています。画像テクスチャがそもそもすくなかったので、そこまで圧縮されていませんが、4.6MBから3.54MBにさらに圧縮させることができました。

基本的には、これで完了になります。MeshはDRACO、テクスチャはKTX2と覚えていれば今回の備忘録は完了です。今回は、glb形式でしたが、FBXモデルの場合、FBXからGLBに直すことが、一番手っ取り早い対処法かと思います。ひとまずはここまで。

参考文献】

WebXRでオススメのKTX2とは|realmetaverse
旧Facebook(現Meta)社が WebXRにおいてKTX2 textureをオススメしている件についてはコチラ↓で触れました。 改めてKTX2について調べてみたいと思います。 Khronos TeXture、略してKTXです。 ハッシ...
【WebGL / three.js】DRACO&Basis圧縮でWebに最適なレベルまで3Dモデルを圧縮する
Google and Binomial Contribute Basis Universal Texture Format to Khronos’ glTF 3D Transmission Open Standard
Earlier today, Google and Binomial announced that they have partnered to open source a sophisticated texture compressor ...
KTX - GPU Texture Container Format
KTX (Khronos Texture) is an efficient, lightweight container format for reliably distributing GPU textures to diverse pl...
タイトルとURLをコピーしました