Open18

Three.js Performance Tuning

Takashi ToyofukuTakashi Toyofuku

Three.jsでfbxを読み込んで3Dモデルをロードするっていう実装をしている。
モデルが読み込まれるまでに数秒ラグがあるのでチューニングしていく

Takashi ToyofukuTakashi Toyofuku

まず一旦ローディング画面を入れるw
Canvasをrelativeとして、absoluteでloadingを乗っける。
とりあえずこんな感じ。

<Loading type="spin" color={Theme.pallet.primary} />
Takashi ToyofukuTakashi Toyofuku

Chrome InspectorのPerformanceタブで時系列を整理。parseにめっちゃ時間がかかってるみたいで、loadingが終わるまで(Groupの生成が終わる)に3秒程度時間がかかっている。
fbx => Groupの変換に時間がかかっていそうなのでまずはGroupをローカルにキャッシュしてみる。

Takashi ToyofukuTakashi Toyofuku

一般的にブラウザでのキャッシュの仕組みとして

  • LocalStorageにjson objectを保存
  • IndexedDBにjson objectを保存
  • ServiceWorker, Cache APIを利用してネットワークリクエストをプロキシし、キャッシュ

という手段がありそう。
Storage for the webによるとLocalStorageは5MB制限があり、早々に脱落。

parseに時間がかかるので、

  • IndexedDBにjson objectを容量を気にしながら(多分件数制限する?)保存
  • ServiceWorker, Cache APIですべてのアセットをバージョニングしてキャッシュ

っていう方向で行くかなと。

参考

Takashi ToyofukuTakashi Toyofuku

まずはIndexedDBにjson objectを保存していく。
ライブラリはDexie(一番最初に見つけただけ)。
Groupをそのまま保存しようとしたらだめだった。多分Serializeが必要。

Takashi ToyofukuTakashi Toyofuku

JavaのSerializeを思い出している。あれはインスタンスの状態をバイト列に変換して復元可能な状態にしている。
よってFunctionとかもSerializeするのって怪しくて、状態だけ取り出してIndexedDBに突っ込めればいいのか。けど取り出した値をGroupにバインドするのってどうすればいいんだ。。。

Takashi ToyofukuTakashi Toyofuku

引数なしコンストラクタならワンチャンできそうな気配でてきたので、FBXLoaderのソース読んで見る。

Takashi ToyofukuTakashi Toyofuku

FBXLoaderが謎のグローバル変数を使っちゃったりしているので厳しいな。。。

Takashi ToyofukuTakashi Toyofuku

FBXLoader絡みは厳しい。FBXだけExporterがないというのも有名な話である。
というわけでルートでGroup作ってコンテキストに入れちゃおう(超妥協)。
毎回ネットワークアクセスが走るとアレなので、

ServiceWorker, Cache APIですべてのアセットをバージョニングしてキャッシュ

もセットにしてやる。

Takashi ToyofukuTakashi Toyofuku

ReactでContext作ってGroup打ち込んだら割と早くなった。
が、cacheしたせいでhookが効かなくなったので各所修正。
結局fbx=>Groupににする際にブラウザが固まってしまうので、SSRが必須かもしれない。

Takashi ToyofukuTakashi Toyofuku

戻ってきた。
あれから対策として

  1. ファイルのGLTF化
  2. DRACO圧縮
  3. SSG化
  4. preload
  5. Drei.Preloadの使用

GLTF化と同時にnextjsのdynamic import機能を使ってサーバー側で処理が落ちないようにした(XMLHttpRequest is not definedみたいになってしまうので)。

を行って、PC環境だとかなり早くいい感じに表示されるようになった。
一方で、iPhoneの古い端末だとテクスチャの一部が読み込めなく、ノーマルマップや目が読み込まれていない表示になるようになったので再度調査中。

今はiPhoneXを借りてUSBでつないでSafariでinspect中。
まず怪しいのはTextureのサイズが4Kになっているので、一旦2Kにする対応を行う。
4Kだとメモリ64MB使うらしい。
Timelinesでのモニタリング結果はこんな感じ。ここから内容を見ていく。

Takashi ToyofukuTakashi Toyofuku

テクスチャはGPUに転送する際にデコードして送る必要があるらしく、それをすっ飛ばすための圧縮テクスチャーというものがあるらしい。
デバイスごとに圧縮フォーマットが異なるのが困った。

https://ics.media/entry/17863/

Takashi ToyofukuTakashi Toyofuku

今ワークフロー的にMaya側でGLTFの出力お願いしているけど、最適化していくにはGLTF変換は結構いろんなオプションがあって、細かめにやっていかないといけなそう