Babylon.jsで多数の3Dモデルを表示すると、うまく表示されないことがあります。M1 Macのような高性能PCではあまり気にならなくても、iPhoneのようなモバイルにするとロードに失敗することがあります。
今まで見落としていたのですが、Babylon.js DocumentationのSceneという項目に、パフォーマンスに関する解説が多数あるのを見つけました。
そこで、今回は4/1の勉強会で使った公園の3Dモデルを元に、パフォーマンス改善できた方法を紹介します。
環境
MacBookPro (M1 chip, Edge)
iPhone 12Pro (Safari, Chrome, Edge)
1. 今回使ったモデル
4/1の勉強会で紹介した3Dモデルです。無料データをもとに加工しました。
Blenderで表示させた時の画面です。赤枠で囲んだように、メッシュの数が100万近くあります。また、何も調整していないので、マテリアルも100個近くあります。glbファイルとしてのファイルサイズは100.7MBであり、とにかく重めなファイルです。
今回はPostProcessを入れずに、シンプルにglbファイルを読み込むだけにしました。これをMacBookProのEdgeブラウザで表示するとこのようになります。
M1 Macの性能がよいためか、120fpsでほぼ安定しています。
しかし、iPhoneのSafari/Chrome/Edgeで表示させると、このように失敗します。
2. パフォーマンス改善例
Babylon.js Documentationのこちらのページを参考にしました。
Optimizing Your Scene | Babylon.js Documentation
いろいろな方法が書かれているのですが、試しても効果がないものもありました。ただ、細かく読みきれてないので使う条件を満たせていないだけの可能性もあります。
今回の3Dモデルで効果があったのは "scene.getAnimationRatio();"の追加でした。
Babylon.js processes speed depending on the current frame rate. On low-end devices animations or camera movement may differ from high-end devices. To compensate this you can use: scene.getAnimationRatio(); The return value is higher on low frame rates.
ここに記載のように、フレームレートが低いデバイスでも描画速度を上げてくれます。これにより、iPhone Safari/Chromeでも表示されるようになりました。
ただし、視点を変えると先ほどの画面になって動かないことがありました。このようなときは起動していないブラウザを終了してから自身を再起動する (例:Chromeを使っている場合、Safariは終了してChromeは再起動) と、また表示されるようになりました。
scene.freezeActiveMeshes(); を使う方法もあります。
Freezing the active meshes
If you are CPU bound, you can decide to keep the list of active meshes unchanged and then free the time spent by the CPU to determine active meshes:
scene.freezeActiveMeshes();
You can unfreeze the active meshes with:
scene.unfreezeActiveMeshes();
Note that you can force a mesh to be in the active meshes before freezing the list with mesh.alwaysSelectAsActiveMesh = true. Freezing active meshes list may cause some things on the scene to stop updating. One example is RenderTargetTexture, if it's used by mesh materials. For that case RTT needs to be explicitly added to the list of active camera's custom render targets, which will guarantee that it ends up in the render list of the scene: camera.customRenderTargets.push(renderTargetTexture);
ただ、ここはもう少し準備をしないとうまく使えないようです。このオプションをつけたところ、iPhoneでも表示はされるのですが一部が欠けてしまいました。
RenderTargetTextureなど、もう少し調べる必要がありそうです。
なお、今回のコードは以下の通りです。
3. おわりに
Babylon.js Documentationのパフォーマンス改善のページには、他にも多数の方法が紹介されています。
まだ調べ始めたばかりなので、また調査が進んだら紹介したいと思います。