[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アバター描画パフォーマンスを大幅に改善するものです。
GitHub - WebXR-JP/avatar-optimizer
3D model optimization library for WebXR applications. VRMアバターのテクスチャアトラス化・メッシュ結合・ポリゴン削減を行うnpmライブラリ。
github.com本記事では、ライブラリの最適化アプローチの解説、実装内容、そしてPC(RTX 4070Ti)およびMeta Quest 3での実測結果をまとめます。
VRM Crowd Benchmark
@webxr-jp/avatar-optimizerによるVRMアバター多体表示ベンチマーク。最適化ON/OFF比較、アニメーション、WebXR対応。
lain-lab.comスクリーンショット(PC)
30種 × 57体表示(最適化ON / PC)
同条件(最適化OFF / PC)
動画(PC)
スクリーンショット(VR)
(最適化ON / VR)
(最適化OFF / VR)
動画(VR)
CREDIT
動作検証で使用したモデルは、自作したもの以外に、衣装データを含め以下のクリエイターの方のモデルを使用させてもらっています。
Author: minipper 様
BOOTH - minipper 様
作ったものとかいろいろ置いときます。Vroid衣装は、コーデ名はAIに考えてもらってます。デザインはちゃんと自分で考えて自分で描いてますがレースとか柄とかに使うテクスチャをたまにAIに作ってもらってます。ソフトウェアはAIと作ったものがほとんどです。
minippa.booth.pmAuthor: おしり服屋さん 様
BOOTH - おしり服屋さん 様
自キャラ用に作成した衣装だけど良かったら使ってねというスタイルの無料配布場です。
oshiri-chan.booth.pmAuthor: SATORI 様
BOOTH - SATORI 様
3Dmodel素材、ロゴ製作、配布販売を行う場所です。何でも屋さん、宜しかったらフォローしてやって下さい^^
official-satori.booth.pmAuthor: 桜田とまこ 様
VRoid Hub - 桜田とまこ 様
絵藍ミツアプロジェクト用にVRoid作成中です。プリセットの色変えモデルですが、自由に使ってください。
hub.vroid.comAuthor: マユラ 様
VRoid Hub - マユラ 様
絵藍ミツアプロジェクト用にVRoid作成中です。プリセットの色変えモデルですが、自由に使ってください。
hub.vroid.com検証結果
PC(RTX 4070Ti + DDR4 64GB)
| 条件 | FPS | Draw Calls | Triangles |
|---|---|---|---|
| 57体 / 最適化ON(Poly 50%, Atlas 2048) | 40 | 496 | 2.2M |
| 57体 / 最適化OFF | 26 | 1420 | 3.4M |
| 57体 / 最適化ON(Poly 25%, Atlas 512) | 41 | 543 | 1.5M |
| 20体 / 最適化ON(1種のみ) | 60 | 141 | 826K |
最適化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-optimizer の loadVRM / 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-animation の createVRMAnimationClip は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時の自律歩行 |
| WebXR | Quest 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ソーシャル、群衆シミュレーション等)における事前検証・パラメータ調整に活用できる。