Version: 2020.3
言語: 日本語
オクルージョンカリングの参考資料
動的解像度

CullingGroup API

CullingGroup は、個人のシステムを Unity のカリングと LOD パイプラインに統合する方法を提供します。この方法は、以下のような多くの目的に使用できます。

  • 群衆シミュレーションで、実際に見えている範囲内で完全に見えているゲームオブジェクトのみを表示したい場合。
  • Graphics.DrawProcedural で GPU パーティクルシステム を構築したいが、壁の背後のパーティクルシステムのレンダリングは省きたい場合。
  • プレイヤーの視界にいきなり敵を登場させないために、敵を生成する場所としてカメラから隠れている位置をトラッキングしたい場合。
  • 近距離用の高品質アニメーションと AI 計算が必要なキャラクターから、遠距離用の低品質の動作をするものに変える場合。
  • シーンに 10,000 個のマーカーポイントが置かれているとき、プレイヤーがマーカーポイントから 1m 以内に近づいたときに効果的に察知したい場合。

境界球の配列を持つことによって API が利用できるようになります。1 台の特定のカメラからとらえられる球状の可視範囲は、LOD のレベルと同じように扱われる “距離幅” の値と一緒に計算されます。

CullingGroup を始めるにあたって

CullingGroup を使用するには、コンポーネントもビジュアルツールも必要ありません。CullingGroup には、スクリプトだけでアクセスできます。

CullingGroup は “new” 演算子で作成できます。

CullingGroup group = new CullingGroup();

CullingGroup で可視計算を行うには、使用されるカメラを指定します。

group.targetCamera = Camera.main;

球の位置と半径とともに BoundingSphere 構造体の配列を作成し、実際に配列内にある球の数とともに SetBoundingSpheres に渡します。球の数は、配列の長さと同じである必要はありません。たとえ、はじめは実際配列に入れる球の数が少なくても、今までで一度に作った球の数で一番多いものでも十分に収まるほど長い配列を作成することが推奨されます。こうすることにより、球の数の増減が発生しても、ランタイムに配列のサイズ変更を行うという高い代償を払わずに済みます。

BoundingSphere[] spheres = new BoundingSphere[1000];
spheres[0] = new BoundingSphere(Vector3.zero, 1f);
group.SetBoundingSpheres(spheres);
group.SetBoundingSphereCount(1);

この時点で、CullingGroup は各フレームごとに球の可視範囲の計算を始めます。

CullingGroup をクリーンアップして、使用したメモリを開放するには、標準の .NET IDisposable を使用します。

group.Dispose();
group = null;

onStateChanged コールバックで結果を得る方法

可視範囲や距離ステートが変わる球に反応する最も効果的な方法は、onStateChanged コールバックフィールドの使用です。これを、CullingGroupEvent 構造体を引数とする関数に設定します。すると、これがカリングが完了した後に、ステートが変わったそれぞれの球に対し呼び出されます。CullingGroupEvent 構造体のメンバーによって、新旧の球のステートがわかります。

group.onStateChanged = StateChangedMethod;

private void StateChangedMethod(CullingGroupEvent evt)
{
    if(evt.hasBecomeVisible)
        Debug.LogFormat("Sphere {0} has become visible!", evt.index);
    if(evt.hasBecomeInvisible)
        Debug.LogFormat("Sphere {0} has become invisible!", evt.index);
}

CullingGroup クエリ API で結果を得る方法

onStateChanged デリゲートに加え、CullingGroup には、境界球配列内の球の最新の可視範囲と距離を求める API があります。球のステートをチェックするには、IsVisible と GetDistance メソッドを使用します。

bool sphereIsVisible = group.IsVisible(0);
int sphereDistanceBand = group.GetDistance(0);

複数の球のステートをチェックするには、QueryIndices メソッドを使用します。このメソッドは、球の連続する範囲をスキャンして、与えられた可視範囲や距離ステートに該当するものを見つけます。

// Allocate an array to hold the resulting sphere indices - the size of the array determines the maximum spheres checked per call
int[] resultIndices = new int[1000];
// Also set up an int for storing the actual number of results that have been placed into the array
int numResults = 0;

// Find all spheres that are visible
numResults = group.QueryIndices(true, resultIndices, 0);
// Find all spheres that are in distance band 1
numResults = group.QueryIndices(1, resultIndices, 0);
// Find all spheres that are hidden in distance band 2, skipping the first 100
numResults = group.QueryIndices(false, 2, resultIndices, 100);

クエリ API で見つけられる情報は、CullingGroup に使用されるカメラが、実際にカリングするときにだけ更新されるということに注意してください。

効率よく CullingGroup API を利用するために

プロジェクトにどのように CullingGroup を利用するか考えるときは、以下の CullingGroup 設計の特徴を考慮してください。

可視範囲の利用

CullingGroup の可視範囲計算で求める容量は、境界球 - 実際には、位置 (球の中心) と半径の値 - によって決定されます。パフォーマンス上の理由で、他のどんな形状の境界も使用できません。つまり、実際的には、カリングするオブジェクトを完全に囲む球を定義するということです。もっとぴったりとフィットさせる必要がある場合は、複数の球でオブジェクトの異なる部分を囲み、すべての球の可視ステートに基づいて判断します。

CullingGroup で可視範囲を判断するために、どのカメラに基づいて可視範囲を計算するかという情報が必要です。現在、1 つの CullingGroup は、1 つのカメラしかサポートしません。複数のカメラに対する可視範囲を判断する場合は、1 つのカメラに対し 1 つの CullingGroup を使用し、それぞれの結果を総合します。

CullingGroup は、円柱カリングと静的オクルージョンカリングのみに基づいて可視範囲を計算します。動的オブジェクトは計算に考慮されません。

距離の利用

CullingGroup は、リファレンス位置 (カメラやプレーヤーの位置など) と各球の最も近い点の間の距離を計算することができます。この距離値は直接的な値ではなく、別個の “距離幅” の整数結果を計算するために、与えられた一そろいの閾値を使用して量子化されます。これらの距離幅を “近距離” “中距離” “遠距離” と解釈して使用することを目的としています。

オブジェクトがある幅から他の幅へ移動するとき、CullingGroup はコールバックを実行します。この機会に、オブジェクトのビヘイビアを CPU 負担の少ないものへ変更したりすることができます。

最後の距離幅を超えるすべての球は不可視とされ、とても遠くにあるオブジェクトを完全に非アクティブ化するカリングを簡単に実施できます。このビヘイビアが不要な場合は、単に最後の閾値を無限に遠い距離に設定します。

各 CullingGroup につき、1 つのリファレンス位置しかサポートされません。

パフォーマンスと設計

CullingGroup API では、シーンを変更して、即座に境界球の新たな可視ステートをリクエストすることはできません。パフォーマンスに関する理由で、CullingGroup は、カメラのカリングを実行している間に新しい可視情報を計算することしかできないからです (コールバックと CullingGroup クエリ API のいずれを使っても、現時点で可能な情報はそれだけです)。つまり、非同期で CullingGroup を使用するということです。

CullingGroup に使用した境界球配列は、CullingGroup によってコピーではなく、参照されます。これはつまり、SetBoundingSpheres に渡す配列への参照を保持し、SetBoundingSpheres を再び呼び出すことなく、この配列のコンテンツを変更できるということです。複数のカメラを使用する場合のように、同セット内の球の可視範囲と距離を計算するために複数の CullingGroup が必要な場合は、すべての CullingGroup に同様の境界球配列インスタンスを共有させると効果的です。

オクルージョンカリングの参考資料
動的解像度