WebGL・three.jsなどで 画像、モデルを圧縮して表示する方法の備忘録です。
古くから採用されているようで、簡単に使うことができますが、あまり知られていないようなので、自分用に、簡単にまとめてみました。
画像を圧縮する
画像を単純に圧縮するのなら、JPEG・PNGを圧縮してもらえるTinypng、Webp形式で保存することが有名ですが、Three.jsやbabylon.jsで画像でテクスチャを使うのあれば、KTX2形式で保存することが有効に働くことがあります。最大の特徴は、圧縮した状態のままGPUに画像テクスチャを持っていけることにあり、JPEG・PNGと比べてGPUメモリを抑えることができます。※こちらを参照


KTX2を使うには、KTX-Softwareから専用のソフトを使い、ターミナル経由でローカルで実行することが、基本ステップになります。(変換できる専用サイトもありますが、ここでは省略させていただきます)
※ただし、3Dモデルのみの使用で、画像単体を変換しないのであればソフトのダウンロード不要です。glTF Transformを使えばインストール不要でKTX2化できます。
Windowsであれば、KTX-Software-4.4.2-Windows-x64.exe をダウンロードしてインストールいたします。DLしたexeを実行して、そのままクリックを進めてインストールが完了できると思います。

インストール完了後、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)
})
})ここまでが、基本的なクイックスタートになります。
toktx --t2 --encode etc1s --clevel 5 --qlevel 128 "public/image/sakura.ktx2" "public/image/sakura.jpg"
KTX-Softwareをインストール後に、ターミナルで使えるようになるコマンドが toktx になります。
–t2 がKTX2使用するというコマンドでBasis Universal対応なので、–t2が推奨されているようです。
ここで、重要になるのが、次の etc1s になります。etc1sの代わりに、uastcというものに変更することができます。
toktx --t2 --encode uastc "public/image/sakura.ktx2" "public/image/sakura.jpg"
uastc では、GPUのメモリ消費がETC1Sより大きくなりますが、画像品質が上がるメリットがあります。しかし、私が使用した限り、変換失敗して0MBになることがあり、実質うまくいかないことが多かったイメージです。
もっと詳しく知りたい方は、コマンドとともにぜひ調べてみてください

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/functionsnpx 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に直すことが、一番手っ取り早い対処法かと思います。ひとまずはここまで。
【参考文献】





