Unity-CullingGroup API
CullingGroup 提供一種將系統(tǒng)集成到 Unity 剔除和 LOD 管線中的方法。這可用于許多目的;例如:
模擬一群人,同時只為現(xiàn)在實際可見的角色提供完整的游戲?qū)ο?/p>
構(gòu)建由 Graphics.DrawProcedural 驅(qū)動的 GPU 粒子系統(tǒng),但是跳過對墻背后的粒子系統(tǒng)的渲染
跟蹤不在攝像機視野范圍內(nèi)的生成點,以便在生成敵人時不讓玩家看到他們“彈入”視圖
將角色從近處的全質(zhì)量動畫和 AI 計算切換到遠處更低質(zhì)量、更低成本的行為
在場景中設(shè)置 10,000 個標記點,并在玩家進入其中任何標記點的 1m 范圍內(nèi)時有效發(fā)現(xiàn)這一狀態(tài)
API 的工作原理是讓您提供一系列包圍球體。然后計算這些球體相對于特定攝像機的可見性,以及可視為 LOD 級別號的“距離帶”值。
CullingGroup 入門
沒有用于處理 CullingGroup 的組件或可視化工具;只能通過腳本訪問它們。
可使用“new”運算符來構(gòu)造 CullingGroup:
CullingGroup group = new CullingGroup();
要讓 CullingGroup 執(zhí)行可見性計算,請指定其應使用的攝像機:
group.targetCamera = Camera.main;
使用球體的位置和半徑來創(chuàng)建并填充 BoundingSphere 結(jié)構(gòu)數(shù)組,并將其與實際位于數(shù)組中的球體數(shù)一起傳遞給 SetBoundingSpheres。球體數(shù)量無需與數(shù)組的長度相同;事實上,我們建議創(chuàng)建一個足夠大的數(shù)組來保存您一次擁有的最多球體(即使您最初在數(shù)組中實際擁有的球體數(shù)量非常少)。這樣就可以避免在添加或刪除球體時調(diào)整數(shù)組大小,這類操作的成本很高。
BoundingSphere[] spheres = new BoundingSphere[1000];
spheres[0] = new BoundingSphere(Vector3.zero, 1f);
group.SetBoundingSpheres(spheres);
group.SetBoundingSphereCount(1);
此時,CullingGroup 將開始計算每幀單個球體的可見性。
要清理 CullingGroup 并釋放它使用的所有內(nèi)存,請通過標準的 .NET IDisposable 機制來處置 CullingGroup:
group.Dispose();
group = null;
通過 onStateChanged 回調(diào)來接收結(jié)果
為響應球體而更改其可見性或距離狀態(tài)的最有效方法是使用 onStateChanged 回調(diào)字段。將其設(shè)置為一個函數(shù),該函數(shù)以 CullingGroupEvent 結(jié)構(gòu)作為參數(shù);對于已改變狀態(tài)的每個球體,將在剔除完成后調(diào)用此函數(shù)。CullingGroupEvent 結(jié)構(gòu)的成員會告訴您球體的先前狀態(tài)和新狀態(tài)。
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 來接收結(jié)果
除了 onStateChanged 委托之外,CullingGroup 還提供一個 API,用于檢索包圍球體數(shù)組中任何球體的最新可見性和距離結(jié)果。要檢查單個球體的狀態(tài),請使用 IsVisible 和 GetDistance 方法:
bool sphereIsVisible = group.IsVisible(0);
int sphereDistanceBand = group.GetDistance(0);
要檢查多個球體的狀態(tài),可使用 QueryIndices 方法。此方法將掃描連續(xù)范圍的球體以查找與指定可見性或距離狀態(tài)相匹配的球體。
// 分配一個數(shù)組來保存生成的球體索引 - 數(shù)組的大小決定每次調(diào)用檢查的最大球體數(shù)
int[] resultIndices = new int[1000];
// 還要設(shè)置一個 int 來存儲已放入數(shù)組的實際結(jié)果數(shù)
int numResults = 0;
// 查找所有可見的球體
numResults = group.QueryIndices(true, resultIndices, 0);
// 查找位于距離帶 1 中的所有球體
numResults = group.QueryIndices(1, resultIndices, 0);
// 查找隱藏在距離帶 2 中的所有球體,跳過前 100 個球體
numResults = group.QueryIndices(false, 2, resultIndices, 100);
請記住,僅在 CullingGroup 使用的攝像機實際執(zhí)行剔除時,才更新查詢 API 檢索的信息。
CullingGroup API 最佳實踐
在考慮如何將 CullingGroup 應用于項目時,請考慮 CullingGroup 設(shè)計的以下方面。
利用可見性
CullingGroup 為其計算可見性的所有體積都由包圍球體定義;實際上,由位置(球體中心)和半徑值定義。出于性能原因,不支持其他包圍形狀。在實踐中,這意味著您將定義一個球體來完全包圍希望剔除的對象。如果需要更緊密擬合,請考慮使用多個球體來覆蓋對象的不同部分,并根據(jù)所有球體的可見性狀態(tài)做出決定。
為了評估可見性,CullingGroup 需要知道應該從哪個攝像機可見性開始計算。目前,單個 CullingGroup 僅支持單個攝像機。如果需要評估多個攝像機的可見性,應為每個攝像機使用一個 CullingGroup 并合并結(jié)果。
CullingGroup 將僅基于視錐體剔除和靜態(tài)遮擋剔除來計算可見性。它不會將動態(tài)對象視為潛在遮擋物。
利用距離
CullingGroup 能夠計算某個參考點(例如,攝像機或玩家的位置)與每個球體上最近點之間的距離。此距離值不會直接提供給您,而是使用您提供的一組閾值來量化,以便計算離散的“距離帶”整數(shù)結(jié)果。目的是將這些距離帶解讀為“近距離”、“中距離”、“遠距離”等。
一個對象從一個區(qū)域移到另一個區(qū)域時,CullingGroup 將提供回調(diào),讓您有機會進行某些操作,例如將該對象的行為更改為 CPU 使用強度較低的操作。
超出最后一個距離的任何球體都將被視為不可見,這使您可以輕松構(gòu)建一個剔除實現(xiàn)來完全停用非常遠的對象。如果不想要此行為,只需將最終閾值設(shè)置為無限遠的距離。
每個 CullingGroup 僅支持一個參考點。
性能和設(shè)計
CullingGroup API 不允許您對場景進行更改后立即請求包圍球體的新可見性狀態(tài)。出于性能原因,CullingGroup 僅在執(zhí)行整個攝像機剔除期間計算新的可見性信息;此時,您可以通過回調(diào)或 CullingGroup 查詢 API 來獲取信息。實際上,這意味著您應該以異步方式處理 CullingGroup。
提供給 CullingGroup 的包圍球體數(shù)組將由 CullingGroup 引用,而不是復制。這意味著您應該保留對傳遞給 SetBoundingSpheres 的數(shù)組的引用,并可修改此數(shù)組的內(nèi)容,而無需再次調(diào)用 SetBoundingSpheres。如果需要多個 CullingGroup 來計算同一組球體的可見性和距離(例如,對于多個攝像機),那么讓所有 CullingGroup 共享相同的包圍球體數(shù)組實例會很高效。