クラスタードライティング
クラスタードライティングは、PlayCanvas Engine v1.56以降にデフォルトで有効になっています。古いライティングシステムは、しばらくの間Engineに残りますが、将来のマイナーリリースで廃止される予定です。
ライトは、アプリケーションにリアリズムを加える良い方法です。しかし、リアルタイムライトは、大量のシャドウを投げるライトが多数存在する場合など、ランタイムパフォーマンスコストを引き起こすことがあります。
パフォーマンスのコストを削減するための解決策の一部は、個々のメッシュに影響を与えるライトの数を制限することです。これは、通常は各オブジェクトに近接したライトを見つけて使用することによって実装されます。しかし、この戦略には複数のデメリットがあります。
- 各オブジェクトが異なるセットのライトを使用するため、カスタムシェーダーをコンパイルする必要があります。
- この戦略が効果的になるには、大きなオブジェクトを小さなオブジェクトに分割する必要があります。
- 大量のシャドウキャストライトは、影マップで使用される可能性のあるテクスチャスロットの使用可能数を超過する可能性があります。
これらの問題に対処するために、PlayCanvasはクラスタードライティングソリューションを使用して、Omni Lights および Spot Lights の高パフォーマンス実装を提供します。ライトに関する情報をテクスチャに格納し、GPUがシェーディングフラグメントに近接するライトのみを簡単に使用できるようにします。クラスタードライティングの複数の利点があります。
- シェーダーは、複数のライトを処理できるため、シーンからライトが追加または削除されたときにシェーダーを再コンパイルする必要はありません。
- (Shadow および Cookiesを含む)多数のライトがシーンで使用できるため、ピクセルごとに近接するライトのみが評価されるため使います。
Directional Lights は、すべてのオブジェクトに影響を及ぼすため、クラスクラスタードライティングソリューションは使用しません。
実装の概要
以下の手順は、クラスタードライティングの実装の基本的な概要を提供します。
-
カメラの錐台によってライトのリストを評価することで、カメラに対して可視なライトのリストを削除します。
-
可視なすべてのライトの軸に沿った境界上に、ワールドスペース3Dグリッドを配置します。
-
3Dグリッドの各セルは、それと交差するライトのインデックスを格納します。CPU上では、この情報は毎フレーム更新され、任意の位置に影響を与えるライトのリストを取得することができます。この情報はテクスチャに格納され、GPUで使用可能になります。
-
すべての可視ライトのプロパティは、別のテクスチャに格納されるため、GPUでアクセス可能になります。
-
シャドウマップとCookieテクスチ ャは、個々のテクスチャではなくアトラスにレンダリングされるため、同時にすべてのテクスチャをシェーダーで使用できます。
-
フラグメントシェーダーでライティングを評価する際に、フラグメントワールドスペース位置を使用して3Dグリッドのセルにアクセスし、格納されたライトを評価します。
エディタオプション
クラスタードライティングのオプションは、エディタ設定の「レンダリング」の下で見つけることができます。
これにより、以前のライティングシステムを使用する必要がある場合にクラスタードライティングを無効にしたり、以下のtune performance and featuresを調整したりすることができます。
クラスタードライティングの調整
機能の有効化
Clustered Lightingシェーダーは、サポートされるすべてのライ トを処理する必要があるため、これらの機能を処理するためのコードを含める必要があります。これにより、シェーダーが必要以上に大きくなり、コンパイルに時間がかかる可能性があります。ユーザーのアプリケーションで必要のない機能を無効にすることができる機能オプションセットがあります。
- 影の有効化 - Shadowsのサポートの有効化/無効化
- Cookiesの有効化 - 軽いCookiesのサポートの有効化/無効化
- Area Lightsの有効化- Area Lightsのサポートの有効化/無効化
3Dグリッドの設定
Cellsプロパティを使用して、各ワールド軸に沿ってセルの数を指定できます。これにより、すべての可視ライトを含むAxis Aligned Bounding Boxが、指定された数のセルに動的に細分化されます。
Max Lights Per Cellプロパティを使用して、各個別セルに格納されるライトの最大数を指定できます。これは、オーバーラップするライトの最大数を表します。通常は、ライトのオーバーラップが大きくなるため、ライトの数を粗いグリッドサブディビジョンに対して増やす必要があります。
アトラスの設定
可視なすべてのシャドウマップとCookieテクスチャは、アトラスに格納されます。Shadows用の1つのアトラステクスチャとCookie用のもう1つのアトラスがあります。アトラスのサイズは異なる場合がありますが、内部的には、個々のライトによって使用される小さな領域に分割されます。
Shadow Atlas Resolutionは、Shadowアトラスサイズを設定するためのものであり、 Cookie Atlas ResolutionはCookieアトラスを設定するためのものです。これらのサイズは2の累乗である必要はありません。
Atlas Splitは、アトラスがライトによって使用される個々のサブテクスチャにどのように分割されるかを制御します。2つの分割戦略があります。
-
-
Automatic – 配列サイズが0として指定されている場合、エンジンは必要に応じてアトラスを自動的に分割し、各可視光を等しくサイズのサブテクスチャに割り当てます。たとえば、フレームに3つのライトが表示される場合、アトラスは2x2サブテクスチャーに分割され、これらの4つのサブテクスチャーのうち3つがライトに割り当てられます。
-
-
- Manual – アトラスを固定数のサブテクスチャに分割できるようにします。これは、サイズが異なる場合があります。数字の配列を使用してセットアップされます。各数値は、垂直および水平の両方で分割を表します。マニュアルアトラススプリットの例については、次のセクションを参照してください。
Configuring Manual Atlas Split
アトラスが手動でどのように分割されるかを理解するために、2つの数字が入った配列を考えてみてください:[2, 2]。配列の最初の数字はアトラスを2x2に分割し、合計で四つの領域にします。配列のその後の数字は、これらの領域を再度分割します。この場合、配列の2番目の数字は既存の領域の1つをさらに2x2(つまり4つの領域)に分割し、合計で7つの領域になります。
以下の画像は、手動でアトラス分割を指定する方法を示しています。
他の例:
- [3, 2] - 最初の数字はアトラスを3x3(9つの領域)に分割します。2番目の数字は、これらの領域の1つを2x2(4つの領域)に分割し、合計で12つの領域になります。
- [4] – アトラスは4x4(16エリア)に分割されます。
手動で細分化する最大の利点は、達成できる詳細度です。一定量のサブテクスチャを設定し、それらをスクリーンスペースのサイズ順にライトに割り当てることができます。これにより、スクリーン上で大きいライトはアトラスの大きな領域を受け取り、遠くの小さなライトはアトラスの小さな領域を使用します。利用可能な領域の数よりも多くのライトがある場合、スクリーンスペースが最も小さいライトは影を落とさないでしょう。
シャドウタイプ
影を投影する全てのライトは同じシャドウタイプを使用します。これにより、シャドウのソフトネスと関連するパフォーマンス影響をグローバルに設定することができます。サポートされているオプションはPCF1、PCF3、PCF5です。詳細は、Shadowsページをご覧ください。
制限事項
内部的には、ライトのインデックスは8ビットで保存されているため、任意のフレームで可視的なライトの最 大数は254(1つのインデックスは予約されています)。将来的には、インデックスを保存するために16ビットを使用し、限度を増やす追加オプションがあるかもしれません。
パフォーマンスに関する考慮事項
- Cell subdivisions は可能な限り小さくすべきです。大きなセルの細分化は、グリッドが毎フレームライトで埋まるときにCPUの使用量を増大させます。これは、各シーンの照明の複雑さに応じて最適化するべきです。最適には、ライトの重なりと各セル内のライトの数を制限するために十分なセルを持つべきです。
- Max Lights Per Cell は可能な限り小さくすべきです。これは、3Dグリッドを保存するために使用されるテクスチャのサイズを制限し、これは毎フレーム更新する必要があります。
- クラスタードライティングを使用するアプリケーションが古いモバイルデバイス上で 遅く動作する 場合は、Shadows(シャドウ)やCookies(クッキー)のような機能を全体的にオフにすることを検討してください。
デバッググリッドのレンダリング
クラスタードライティングのデバッグとパフォーマンスチューニングを支援す るために、Layer IDにレンダーするレイヤーをLightingParamsの debugLayer に割り当てます。
// Assuming being in a script type
this.app.scene.lighting.debugLayer = this.app.scene.layers.getLayerByName("World").id;
そして、レンダリングを停止するには、debugLayer
プロパティに undefined
を代入します。
// Assuming being in a script type
this.app.scene.lighting.debugLayer = undefined;