Three.js Performance Tuning
Three.jsでfbxを読み込んで3Dモデルをロードするっていう実装をしている。
モデルが読み込まれるまでに数秒ラグがあるのでチューニングしていく
まず一旦ローディング画面を入れるw
Canvasをrelativeとして、absoluteでloadingを乗っける。
とりあえずこんな感じ。
<Loading type="spin" color={Theme.pallet.primary} />
Chrome InspectorのPerformanceタブで時系列を整理。parseにめっちゃ時間がかかってるみたいで、loadingが終わるまで(Groupの生成が終わる)に3秒程度時間がかかっている。
fbx => Groupの変換に時間がかかっていそうなのでまずはGroupをローカルにキャッシュしてみる。
これよく見ると、morphTargets
にめっちゃ時間かかってるな
一般的にブラウザでのキャッシュの仕組みとして
- LocalStorageにjson objectを保存
- IndexedDBにjson objectを保存
- ServiceWorker, Cache APIを利用してネットワークリクエストをプロキシし、キャッシュ
という手段がありそう。
Storage for the webによるとLocalStorageは5MB制限があり、早々に脱落。
parseに時間がかかるので、
- IndexedDBにjson objectを容量を気にしながら(多分件数制限する?)保存
- ServiceWorker, Cache APIですべてのアセットをバージョニングしてキャッシュ
っていう方向で行くかなと。
参考
まずはIndexedDBにjson objectを保存していく。
ライブラリはDexie(一番最初に見つけただけ)。
Groupをそのまま保存しようとしたらだめだった。多分Serializeが必要。
IndexedDBは構造化複製アルゴリズムというものを採用している。複雑なJSオブジェクトをコピーするためのアルゴリズム。
Functionは複製できず、複製しようとすると例外が出るとのこと。
エラーメッセージをみると、onRotationChange
の複製に失敗しており、Object3D
の最初のメソッドなのでここで失敗してそう。
JavaのSerialize
を思い出している。あれはインスタンスの状態をバイト列に変換して復元可能な状態にしている。
よってFunctionとかもSerialize
するのって怪しくて、状態だけ取り出してIndexedDBに突っ込めればいいのか。けど取り出した値をGroup
にバインドするのってどうすればいいんだ。。。
引数なしコンストラクタならワンチャンできそうな気配でてきたので、FBXLoaderのソース読んで見る。
FBXLoader
が謎のグローバル変数を使っちゃったりしているので厳しいな。。。
FBXLoader
絡みは厳しい。FBXだけExporterがないというのも有名な話である。
というわけでルートでGroup作ってコンテキストに入れちゃおう(超妥協)。
毎回ネットワークアクセスが走るとアレなので、
ServiceWorker, Cache APIですべてのアセットをバージョニングしてキャッシュ
もセットにしてやる。
ReactでContext作ってGroup打ち込んだら割と早くなった。
が、cacheしたせいでhookが効かなくなったので各所修正。
結局fbx=>Groupににする際にブラウザが固まってしまうので、SSRが必須かもしれない。
メモ。
dracoの圧縮も使えるのか。
戻ってきた。
あれから対策として
- ファイルのGLTF化
- DRACO圧縮
- SSG化
- preload
- Drei.Preloadの使用
GLTF化と同時にnextjsのdynamic import機能を使ってサーバー側で処理が落ちないようにした(XMLHttpRequest is not definedみたいになってしまうので)。
を行って、PC環境だとかなり早くいい感じに表示されるようになった。
一方で、iPhoneの古い端末だとテクスチャの一部が読み込めなく、ノーマルマップや目が読み込まれていない表示になるようになったので再度調査中。
今はiPhoneXを借りてUSBでつないでSafariでinspect中。
まず怪しいのはTextureのサイズが4Kになっているので、一旦2Kにする対応を行う。
4Kだとメモリ64MB使うらしい。
Timelinesでのモニタリング結果はこんな感じ。ここから内容を見ていく。
テクスチャはGPUに転送する際にデコードして送る必要があるらしく、それをすっ飛ばすための圧縮テクスチャーというものがあるらしい。
デバイスごとに圧縮フォーマットが異なるのが困った。
これを見る限りGLTF×圧縮テクスチャは道半ばに見える
このGLTF extensionがよさそう
KHR_texture_basisu
今ワークフロー的にMaya側でGLTFの出力お願いしているけど、最適化していくにはGLTF変換は結構いろんなオプションがあって、細かめにやっていかないといけなそう