はじめに
visionOSでのアプリ開発における3Dモデル(以下モデル)の最適化について書きます。
モデルの容量肥大化の原因になりやすい三角形、テクスチャなどに注目し、不要な箇所を削減するなどして最適化を行うことはアプリのパフォーマンスに強く影響を与えます。
最適化を行わずに容量が不用意に大きいままシーンに設置すると、アプリがクラッシュし、最悪の場合はVision Pro本体が強制終了する可能性があります。
背景
約4ヶ月に及んだ企業案件のvisionOSアプリ開発を担当しました。モデルについての知見が浅く、操作中にVision Proが落ちてしまう事象が発生しました。
今回の反省をもとに、モデルの最適化について調べたので執筆しようと思いました。
参考
https://developer.apple.com/videos/play/wwdc2024/10186
モデル容量削減のアプローチ
三角形の合計値
Appleの公式動画でImmersive Sceneに設置するモデルの三角形の合計の推奨値が言及されています。
Immersive Sceneでは三角形の数を50万個以下とし、Shared Spaceのアプリでは25万個以下にすることを推奨します。
この上限はビューアに表示される内容に常に左右されることに注意してください。
〜
安全な目標値を定める場合、ビュー内の三角形の数は上限まで十分余裕のある10万個程度にすることをお勧めします。
シーンに設置するモデルの三角形と頂点数の合計はReality Composer Proで確認できます。下のパネルのタブをStatisticsに設定するとシーン内の各データの数量が表示されています。その中のGeometryの項目のTrianglesに表示されています。

今回の案件で用意したモデルは合計すると約850万個という、推奨値50万個の17倍ありました。結果、アプリ操作中にビュー全体がカクつき、操作を続けているとVision Proが落ちて、起動時の状態に強制的に戻される事態になってしまいました。
空間内に配置したモデルの三角形の数に常に注意しましょう。
視線が通らない箇所のメッシュを省略する
公式サンプルアプリを見ると床に凹凸をつけたり山を周囲に配置することで、視線が通らない箇所を意図的に作りメッシュを省略することで三角形の合計を削減しています。


仮想空間を作る際はユーザーの移動範囲を考慮し、視線があたらない部分を省略できないか検討しましょう。
ユーザーとモデルの距離とテクスチャ解像度の関係
ユーザーと距離があるメッシュのテクスチャの解像度を調整しましょう。不自然に見えない程度に解像度を下げることで負荷を減らすことができます。
Reality Composer Proから設定はできないため、Blender等の編集ツールでUVマッピング領域を各メッシュに設定してあげましょう。
テクスチャパッキング
テクスチャのグレースケール画像(Roughness、Metallic、AnbientOcclusionなど)はRGBのそれぞれのチャネルに割り当てて一つの画像で用意しましょう。
Shader GraphのSeparateノードで分割しそれぞれの値に渡します。テクスチャパッキングされた画像だけ用意すれば良いため容量の節約になります。


マテリアルのインスタンス化
同じマテリアルを使う際はコピペではなくインスタンス化しましょう。特に、カスタムマテリアルを使用する際は入力値を外部から変更できるようにして、それぞれ変更できる状態にしておきます。
Shader Graphを開き、入力値を選択して左クリックし、Promote to Materialを選択し外部入力を可能にします。(右の操作パネルからNew Inputをクリックして入力値を新規作成した場合は自動でこの状態になります)

そのマテリアルを左クリックし、Create Instanceをクリックすると外部入力を受け付けた状態のマテリアルが複製されます。これで個別に入力値を設定することができるようになります。
インスタンス化されたマテリアルはインスタンス元と同じシーンに存在しないと機能しません。そのため、マテリアルの設定は個別のモデル内ではなく、シーンファイル内に設置し、一つのファイルでまとめておいたほうが良いです。

透過度
透過度のあるモデルは使わないことが推奨されています。不透明モデルよりも計算回数が多くなり負荷が大きくなると公式で言及されております。また、ポリゴンの三角形の数が増えるより透過度があるほうが負担が大きいです。
また、負荷の問題だけでなく、表示の不具合も起きる可能性があります。
例えば背景画像の中に透過度を与えたオブジェクトを設置した場合、オブジェクトの中が背景ではなく現実空間が表示される不具合があります。以下の動画ではFull Immersive Spaceで開いているため、真っ黒な背景が表示されているのがわかります。
各Entity間の描画順を管理するコンポーネントにModelSortGroupComponentがありますが、適用しても変化ありませんでした。
ベストプラクティスはなく、Appleの公式動画でも透過度のあるモデルは極力使わないように、と言及されています。
照明のないシーンであっても考慮すべきことは、アルファ透過の使用です 。アルファ透過度の使用はできる限り控えてください。GPUは 透明度のレイヤーごとに同じピクセルを何度も計算する必要があるため、 透明なマテリアルはコストがかかります 。これはオーバードローと呼ばれます。これは、透明レイヤーが何層にも重なっている場合に特に問題となります 。
背景画像(Sky dome)
高解像度の画像を使用しましょう。公式では横幅8192pxを推奨しています。
見えない箇所はトリミングしましょう。
ピクセルシェーダーのコストを削減するためにunlitマテリアル(ツヤなしマテリアル)を使用しましょう。
Full Immersive空間環境下におけるIBL(Image Based Light)
背景画像でSky domeを設置する場合はその背景画像の低画質版をIBLの画像にするのが望ましいです。
反射に対して高いクオリティを求めない場合は横幅512pxくらいの比較的低画質のもので問題ありません。高さについては公式の言及はなく、不要な部分をトリミングすることも考えて実際にアプリで表示して都度修正するのが良いかもしれません。
(その他)複数のメッシュを統一する
メッシュが複数ある場合は、一つの親メッシュの中に入れましょう。
アプリ実行中にメッシュに対して特定の処理をする際、複数ある内の一つのメッシュだけが適用されてしまい、他のメッシュが適用されないケースがありました。
全てのメッシュに適用するために再起的に処理するコードを書く必要がありますが、親メッシュを指定すればその様な複雑な処理は必要ありません。
終わりに
モデルの容量はアプリのパフォーマンスだけでなく、Vision Pro本体のメモリ、CPU、GPUなどに強い影響を与えます。三角形の数などは注意してモデルを作らないと推奨値の50万個をすぐに上回ってしまうため細心の注意と創意工夫が求められます。
以上です。visionOSのアプリ開発に参考になれば幸いです。
