3DモデルをWebで配信する際、ファイルサイズは重要な課題です。本記事では、Draco圧縮 を使ってGLBファイルを87%削減した事例と、圧縮時の注意点(特にUV座標)について解説します。

https://3dtiles-viewer.vercel.app/glb-viewer.html

使用データ

  • モデル : Rotunde Brunnen(噴水のある円形建築物)
  • 出典 : Sketchfab
  • 形式 : GLB (glTF 2.0 Binary)

Draco圧縮とは

DracoはGoogleが開発したオープンソースの3Dメッシュ圧縮ライブラリです。glTF 2.0ではKHR_draco_mesh_compression拡張として標準サポートされています。

圧縮の仕組み

  1. 量子化(Quantization) : 頂点座標やUV座標を指定ビット数に丸める
  2. 予測符号化 : 隣接頂点との差分を予測して符号化
  3. エントロピー符号化 : 予測誤差を効率的に圧縮

圧縮コマンド

# gltf-transformを使用
npx gltf-transform draco input.glb output-draco.glb

# オプション付き(高品質設定)
npx gltf-transform draco input.glb output-draco.glb \
  --quantize-position 14 \
  --quantize-normal 10 \
  --quantize-texcoord 12

圧縮結果の比較

ファイルサイズ

ファイルサイズ削減率
rotunde-brunnen.glb(元)94.7 MB-
rotunde-brunnen-draco.glb12.5 MB87%削減

メッシュ構造

項目元ファイルDraco圧縮後
メッシュ数382(統合)
三角形数約175万約167万
テクスチャ1024x1024 PNG同一
バウンディングボックスほぼ同一ほぼ同一

精度

視覚的な精度低下はありません

  • バウンディングボックスがほぼ同一 → 形状は保持
  • デフォルトの量子化設定(位置14bit)で十分な精度
  • 科学計測用途でなければ問題なし

HTTP転送時の圧縮との関係

Webサーバー(Vercel、Nginx等)は、HTTPレスポンスに対してBrotligzip 圧縮を自動適用します。Draco圧縮との関係を実測値で確認しました。

実測: Vercelでの転送サイズ

ファイルファイルサイズ転送サイズ (Brotli)Brotli効果
元ファイル94.7 MB58.5 MB38%削減
Draco圧縮12.5 MB12.4 MBほぼ0%
# 元ファイルのHTTPレスポンスヘッダー
content-encoding: br
content-length: 61338605  # 約58.5MB

# Draco圧縮ファイルのHTTPレスポンスヘッダー
content-encoding: br
content-length: 12991149  # 約12.4MB

なぜDracoファイルにBrotliが効かないのか

Draco圧縮は内部でエントロピー符号化 (データの冗長性を除去)を行っています。これはBrotliやgzipと同様の原理のため、二重圧縮の効果がほとんどない のです。

総合比較: 実際の転送効率

項目元ファイルDraco
ファイルサイズ94.7 MB12.5 MB
転送サイズ(Brotli適用後)58.5 MB12.4 MB
実質的な転送削減-79%削減
ダウンロード時間 (10Mbps)~47秒~10秒
ダウンロード時間 (100Mbps)~4.7秒~1秒

Dracoを使うメリット(転送観点)

  1. 転送サイズが小さい → ダウンロード高速化
  2. サーバーストレージ削減 → ホスティングコスト削減
  3. Brotli圧縮のCPU負荷が不要 → サーバー負荷軽減
  4. CDNキャッシュ効率向上 → 小さいファイルはキャッシュヒット率向上

注意: ストリーミングには非対応

GLBファイルは全データをダウンロードしてからレンダリング されます。プログレッシブロード(徐々に表示)には対応していません。

プログレッシブロードが必要な場合は以下を検討:

  • 3D Tiles : LOD(Level of Detail)による段階的読み込み
  • glTF + EXT_meshopt_compression : メッシュの段階的デコード

量子化とUV座標の問題

問題: UV座標が[0,1]範囲外の場合

Draco圧縮で最も注意が必要 なのがUV座標です。

テクスチャのタイリング(繰り返し)を使用するモデルでは、UV座標が[0,1]の範囲を超えることがあります。

例: タイリング用UV
u: 0.0 ~ 10.0 (10回繰り返し)
v: 0.0 ~ 10.0

なぜ問題が起きるのか

Draco圧縮は量子化 を行います。デフォルトでUV座標は10〜12bitに量子化されます。

UV範囲10bit量子化での精度影響
[0, 1]1/1024 ≈ 0.001問題なし
[0, 10]10/1024 ≈ 0.01軽微なズレ
[0, 100]100/1024 ≈ 0.1目立つズレ

発生する問題

  1. テクスチャの継ぎ目(シーム)
  2. テクスチャのズレ・歪み
  3. タイリング境界での不連続

解決策

1. UV座標の量子化ビット数を増やす

npx gltf-transform draco input.glb output.glb \
  --quantize-texcoord 14  # デフォルト10→14に増加
ビット数ステップ数[0,100]範囲での精度
101,0240.0977
124,0960.0244
1416,3840.0061
1665,5360.0015

2. UVを[0,1]範囲に正規化してからタイリング

// シェーダー側でタイリング
vec2 tiledUV = fract(uv * 10.0); // 10回繰り返し

3. 大きなUV範囲のメッシュはDraco圧縮しない

# 特定のメッシュを除外(手動で分離が必要)

gltf-transformの便利なコマンド

ファイル情報の確認

# 詳細情報を表示
npx gltf-transform inspect model.glb

# 検証
npx gltf-transform validate model.glb

その他の最適化

# テクスチャ圧縮(WebP)
npx gltf-transform webp input.glb output.glb

# テクスチャリサイズ
npx gltf-transform resize input.glb output.glb --width 1024 --height 1024

# 重複頂点の削除
npx gltf-transform dedup input.glb output.glb

# 最適化パイプライン(複数処理を一度に)
npx gltf-transform optimize input.glb output.glb

ビューアでの読み込み

Three.jsでDraco圧縮GLBを読み込む場合、DRACOLoaderの設定が必要です。

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

const loader = new GLTFLoader();

// Dracoデコーダーの設定
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');
loader.setDRACOLoader(dracoLoader);

// 読み込み
loader.load('model-draco.glb', (gltf) => {
  scene.add(gltf.scene);
});

まとめ

項目内容
圧縮率80〜90%のサイズ削減が可能
精度通常用途では視覚的な劣化なし
注意点UV座標が[0,1]範囲外の場合は設定調整が必要
ツールgltf-transformが便利

推奨設定

# 一般的なWeb配信用
npx gltf-transform draco input.glb output.glb

# 高精度が必要な場合
npx gltf-transform draco input.glb output.glb \
  --quantize-position 16 \
  --quantize-normal 12 \
  --quantize-texcoord 14

# UV範囲が広い場合
npx gltf-transform draco input.glb output.glb \
  --quantize-texcoord 16

参考リンク