Toca Boca が高性能でスケーラブルなレンダリングバックエンドを構築した方法

私たちの野心的なモバイル3Dマルチプレイヤーゲーム、Toca Boca Daysのパフォーマンス要求を満たすために、特に低スペックのデバイスで、革新が必要でした。このゲーム(現在は保留中)は、広大なレベルを特徴とし、最大32人のプレイヤーをサポートし、高度にカスタマイズ可能なアバターを提供していました。
2022年末に、私たちはUnityのBatchRendererGroupAPIに基づいて構築されたバッチレンダラーのバックエンドシステムを立ち上げました。この新しいシステムは、重要なパフォーマンス向上をもたらし、私たちはそれを引き続き洗練させています。
この記事では、私たちの次回のゲームで使用する準備をしながら、このシステムのアーキテクチャに関する洞察を共有します。
データアーキテクチャの概要
高レベルでは、私たちのシステムはGameObjectとそのレンダラーコンポーネントのデータを3つのレイヤーに分割します。

最初のレイヤーでは、各レンダラーコンポーネントが単一のカリングインスタンスに対応しています。このレイヤーには、カメラのカリングプレーン、レイヤーマスク、および親LODグループに基づいて可視性を決定するために必要なすべてのデータが含まれています。
第二のレイヤーは、各レンダラーのマテリアルを表し、すべてのレンダラー-マテリアルペアが描画インスタンスを形成します。描画インスタンスは、メッシュやマテリアルなど、レンダリング関連のデータを保存します。
第三のレイヤーは、BatchRendererGroupバッチ(したがって、名前はBatch Renderer)とそのインスタンスで構成されています。各バッチには、GraphicsBufferがあり、ビルトインのインスタンスごとのプロパティ(ObjectToWorld行列など)を含み、オプションでインスタンスごとのマテリアルプロパティのセットを持っています。
レンダラーのインスタンスごとのマテリアルプロパティのセットが、そのバッチタイプを決定します。

私たちのシステムは、CPU側のすべてのデータをコンパクトでデータ指向の配列構造(SoA)レイアウトに保存します。可能な限りネイティブコンテナ内のブリタブル型に依存し、インスタンスカリングや描画コマンド生成などのタスクに高性能のバーストジョブを使用できるようにしています。このアーキテクチャは、システムが多数のインスタンスを効率的に処理できるようにします。
インスタンスごとのマテリアルプロパティ
レンダラーにインスタンスごとのマテリアルプロパティを割り当てるために、「プロパティセット」ScriptableObjectアセットを参照するコンポーネントを追加します。プロパティセットの数は制限すべきです。各セットは別のバッチタイプを定義するため、異なるバッチのインスタンスは一緒にレンダリングされません。
より簡単な作成のために、初期インスタンスプロパティ値を設定するコンポーネントとカスタムインスペクタウィンドウを提供します。インスタンスごとのプロパティがない場合、これらの値はユニークなマテリアルに設定する必要があり、バッチ処理の効率が低下します。

スキニングとスケルトン
Toca Boca Daysでは、アバターは広範なキャラクターカスタマイズを特徴としています。さまざまな衣服やアクセサリーをサポートするために、各アバターのメッシュのスキン部分を17のスキンメッシュレンダラーコンポーネント(例:左上腕)に分割します。ほとんどの衣服アイテムは追加のスキンレンダラーコンポーネントを追加し、プレイヤー数が増えるとすぐに累積します。

私たちのアバターは複雑なため、コストが高くなるシャドウマップには使用しないようにしています。代わりに、ブロブのような影を使用し、ほとんどの場合、アバターはフレームごとに一度だけレンダリングされます。これは、Unityのビルトイン頂点スキニングコストを単一のメッシュに対する複数の描画コマンドに分散できないことを意味します。
代わりに、頂点シェーダーでスキニングを直接処理します。すべてのスキニング行列は単一のグローバルGraphicsBufferに保存され、各レンダラーはこのバッファへのオフセットを受け取り、インデックスごとのボーンインデックスを調整し、それに対応するスキニング行列を取得します。
スケルトンオフセットはBatchRendererGroupインスタンスデータとして保存され、同じメッシュを共有するキャラクター間でバッチ描画コールを行うことができ(例:すべての右上腕を一緒に)、パフォーマンスが向上します。
スキニング行列を計算するには、通常、レンダラー数 × レンダラーごとの平均ボーン数の計算が必要です。これを減らすために、2つの戦略を組み合わせます。
アバター内のすべてのスキンメッシュレンダラーコンポーネントが一貫したボーンインデックスを使用することを確認します。例えば、ボーンインデックス2は常に胸を指し、右足のレンダラーがそれを参照しない場合でも同様です。
メッシュを前処理して、一意のボーンインデックスのセットとバインドポーズ行列を特定し、理想的にはアバターごとに単一のセットを得ることができます。

このアプローチを使用することで、アバターのすべてのスキンメッシュレンダラーコンポーネントのスキニングを単一の「アニメーションスケルトン」と「レンダラスケルトン」のペアに統合できます。

各フレームで、私たちのスケルトンマネージャーは次のステップを実行します。
アニメーション抽出:アニメーションジョブは、PlayablesAPI(Mecanim)を介してアニメーションスケルトンから現在のボーン変換を取得します。
ポーズ計算:バーストジョブは、ボーン階層と抽出された変換からアニメーションポーズ行列を生成します。
スキニング行列の組み立て:バーストジョブは、アニメーションポーズとバインドポーズ行列をスキニング行列に結合します。
GPUアップロード:新しいスキニング行列は、コンピュートシェーダーを使用してグローバルGraphicsBufferにアップロードされます。
このプロセスは複雑ですが非常に効率的で、裸のアバターのスキニング行列を2倍に、衣服が適用されると4倍以上に削減します。
今後の予定
Toca Boca Daysのためのカスタムレンダリングバックエンドを開発し、現在Unity 6.3のためにそれを近代化することは価値があることが証明されています。Unity 2022.3 へのアップグレードは滑らかで、私たちのバッチレンダラーシステムは、広範なキャラクターカスタマイズとインスタンスごとのマテリアルプロパティを持つゲームに対して強力なパフォーマンスを提供し続けています。
BatchRendererGroup API を介して描画コマンド生成を完全にコントロールできることは、オクルージョンポータルやカスタムオクルージョンカリングなどのカスタマイズされたカリング戦略や、カメラの近接性と重要性に基づいてスキニング行列の更新をスロットリングするようなアニメーションの最適化を可能にします。
Unity の拡張性は、ユニバーサルレンダーパイプライン (URP) やソースアクセスを持つ GPU レジデントドロワーのようなパフォーマンスに優れた即時利用可能なソリューションと組み合わさり、私たちが高いパフォーマンス目標を達成しつつ、非常に最適化されたカスタムレンダリングパイプラインを維持することを可能にしました。

Toca Boca は、まだ発表されていないゲームを開発中です。スタジオの最新情報は、Instagram、TikTok、YouTube でフォローしてください。モバイル Unity 開発者のストーリーをもっと探るには、私たちの モバイルソリューションページ と リソースハブ をご覧ください。
