[Astro] #128 VRM Crowd Benchmark — @webxr-jp/avatar-optimizer によるVRMアバター多体表示の最適化と限界検証

[Astro] #128 VRM Crowd Benchmark — @webxr-jp/avatar-optimizer によるVRMアバター多体表示の最適化と限界検証

概要

WebXR-JP の HALBY さん(@halby24)が開発・公開している @webxr-jp/avatar-optimizer を使って、ブラウザ上で多数のVRMアバターを同時表示するベンチマークツールを構築しました。

このライブラリは、VRMモデルのテクスチャアトラス化・メッシュ結合・カスタムシェーダー(MToonAtlasMaterial)によるDraw Call削減を行い、WebブラウザやVRデバイス上でのVRMアバター描画パフォーマンスを大幅に改善するものです。

本記事では、ライブラリの最適化アプローチの解説、実装内容、そしてPC(RTX 4070Ti)およびMeta Quest 3での実測結果をまとめます。

スクリーンショット(PC)

30種 × 57体表示(最適化ON / PC)

VRM Crowd Benchmark — 30種57体表示(最適化ON)

同条件(最適化OFF / PC)

VRM Crowd Benchmark — 30種57体表示(最適化OFF)

動画(PC)

VRM Crowd Benchmark — アバター読み込みとアニメーション再生

スクリーンショット(VR)

(最適化ON / VR)

VRM Crowd Benchmark — VR(最適化ON)

(最適化OFF / VR)

VRM Crowd Benchmark — VR(最適化OFF)

動画(VR)

VRM Crowd Benchmark — アバター読み込みとアニメーション再生

CREDIT

動作検証で使用したモデルは、自作したもの以外に、衣装データを含め以下のクリエイターの方のモデルを使用させてもらっています。

Author: minipper 様

Author: おしり服屋さん 様

Author: SATORI 様

Author: 桜田とまこ 様

Author: マユラ 様

検証結果

PC(RTX 4070Ti + DDR4 64GB)

条件FPSDraw CallsTriangles
57体 / 最適化ON(Poly 50%, Atlas 2048)404962.2M
57体 / 最適化OFF2614203.4M
57体 / 最適化ON(Poly 25%, Atlas 512)415431.5M
20体 / 最適化ON(1種のみ)60141826K

最適化ONにより Draw Calls を 65%削減、FPS を 約70%向上(26→40〜44)。

一方で、ポリゴン削減率やアトラス解像度を変更しても(Poly 50%→25%, Atlas 2048→512)、Draw Callsはほぼ変わらずFPSも1程度の改善に留まった。WebGLにおける多体VRM表示のボトルネックは、ポリゴン数やテクスチャサイズではなく JS→WebGLドライバ間のDraw Call発行オーバーヘッド であることが確認できた。

Meta Quest 3(WebXR / ステレオレンダリング)

条件結果
5体 / 最適化ON(Poly 20%, Atlas 512)動作(FPS維持)
6体以上ブラウザクラッシュ

VRモードでは左右2眼分のステレオレンダリングによりDraw Callsが実質2倍になるため、PCの約1/10が現実的な上限。

avatar-optimizer の最適化アプローチ

1. テクスチャアトラス化

VRMモデルが持つ複数のMToonMaterialのテクスチャを、矩形詰め込み(Bin Packing)アルゴリズム(rectpack-ts)で1枚のアトラス画像に統合する。二分探索で最小サイズに収める最適化も行われる。

2. UV座標の再計算

テクスチャが1枚にまとまったことで、各メッシュの頂点UV座標をアトラス上の新しい位置・スケールに合わせて書き換える。

3. レンダリングモード別のメッシュ結合

Opaque(不透明)、Cutout(くり抜き)、Transparent(半透明)ごとにメッシュをグループ化し、BufferGeometryUtils.mergeGeometries で結合。VRM Expressionで制御される顔メッシュは結合から除外される。

4. MToonAtlasMaterial(核心部分)

カスタムShaderMaterialで、複数マテリアルのパラメータ(色、係数など)を1枚の「パラメータテクスチャ」に焼き込む。各頂点が mtoonMaterialSlot 属性でパラメータテクスチャの何行目を参照するか指定することで、見た目は複数マテリアルだが内部的には1つのDraw Callで描画 される。

実装内容

基本構成

vanilla JavaScript + Three.js のAstroページとして実装。React / R3F は使わず、@webxr-jp/avatar-optimizerloadVRM / optimizeModel APIを直接使用。

import { loadVRM, optimizeModel } from '@webxr-jp/avatar-optimizer'

const loadResult = await loadVRM(source)
const vrm = loadResult.value

await optimizeModel(vrm, {
  migrateVRM0ToVRM1: true,
  atlas: { defaultResolution: 2048 },
  simplify: { targetRatio: 0.5, targetError: 0.01, morphTargetHandling: 'skip' },
})

optimizeModel はVRMオブジェクトをin-place変更する。戻り値は統計情報であり、新しいVRMオブジェクトではない点に注意。

アニメーション対応

@pixiv/three-vrm-animationcreateVRMAnimationClip はUUIDベースのトラック名を生成するが、optimizeModel 適用後やSkeletonUtils.clone後はUUIDが変わるため動作しない。

解決策として、VRMAnimationオブジェクトの humanoidTracks に直接アクセスし、humanoid.getRawBoneNode() でボーン名ベースのAnimationClipを自前で構築する方式を採用した。

function createNameBasedClip(vrmAnimation, avatar) {
  const humanoid = avatar.sourceVrm.humanoid
  const tracks = []

  // VRMAnimationの生データから直接ボーン名マッピング
  for (const [boneName, track] of vrmAnimation.humanoidTracks.rotation.entries()) {
    const node = humanoid.getRawBoneNode(boneName)
    if (node?.name) {
      const t = track.clone()
      t.name = `${node.name}.quaternion`
      tracks.push(t)
    }
  }

  return new THREE.AnimationClip('anim', vrmAnimation.duration, tracks)
}

機能一覧

カテゴリ内容
モデル読み込みlist.json自動読み込み / ドラッグ&ドロップ / ファイル選択(複数対応)
表示数制御スライダー + 数値入力によるリアルタイム増減(オンデマンドロード)
最適化パラメータポリゴン残存率(10〜100%)、アトラス解像度(512〜4096)を動的変更
アニメーションT-Pose / Walk / Idle / Random切替、Walk時の自律歩行
WebXRQuest 2/3 対応、HTMLMeshによるVR内Stats表示、コントローラー操作
ベンチマークFPS / Draw Calls / Triangles / Models数 / 最適化状態のリアルタイム表示
クレジットlist.jsonの作者情報から重複排除・リンク付きで自動生成

VR対応

Three.jsの HTMLMesh + InteractiveGroup で既存のHTML UI(スライダー、Stats)をそのまま3D空間にメッシュとして配置。コントローラーの右スティック上下で表示数増減、Yボタンでアニメーション切替を実装。

ただし HTMLMesh のレイキャスト操作はThree.js r185では不安定で、レーザーポインターによるスライダー操作は動作しなかった。表示専用として割り切り、操作はコントローラーのボタン/スティックに委ねる設計とした。

技術スタック

  • @webxr-jp/avatar-optimizer — VRMモデルの最適化(テクスチャアトラス、メッシュ結合、MToonAtlasMaterial)
  • Three.js — 3Dレンダリング、WebXR、SkeletonUtils
  • @pixiv/three-vrm — VRM読み込み・ヒューマノイドボーン制御
  • @pixiv/three-vrm-animation — VRMAアニメーション読み込み
  • Astro — ページビルド・スクリプトバンドル

まとめ

avatar-optimizerによるDraw Call削減は確実に効果があり、WebGLでのVRM多体表示において約65%のDraw Call削減と70%のFPS向上を実証できた。

ただし、WebGLの構造的な限界(JS→ドライバ間のオーバーヘッド、SkinnedMeshのバッチング不可)により、PCで50〜60体、Quest 3で5体が現実的な上限であることも判明した。これを超えるにはWebGPUへの移行、InstancedSkinnedMesh、またはLOD(遠距離billboard差し替え)といったアプローチが必要になる。

本ツールは、多人数VRM表示が必要なプロジェクト(VRソーシャル、群衆シミュレーション等)における事前検証・パラメータ調整に活用できる。