大家好,這里是七七,前段時間在忙一些事情,最近終于有空來更新優化篇了。本文本打算分為上下兩篇,但為了看更方便,就多花了幾天寫成一文發布,具體是介紹了圖形優化中批處理的具體效果,雖然本文篇幅較大,但還是建議尋求Unity進階或圖形進階的讀者仔細閱讀。
話不多說,開始介紹
介紹
在3D圖形和游戲中,批處理是一個非常通用的術語,它描述了將大量任意數據塊組合在一起并將它們作為單個大數據塊進行處理的過程。這對于CPU,特別是GPU來說是理想的,因為它可以使用多個內核同時粗糲多個任務。在內存中不同位置來回切換內核是需要時間的,因此切換內核所花的時間越少越好。
在某些情況下,批處理的對象指的是網格、頂點、邊、UV坐標和其他用于描述3D對象的不同數據類型的大集合。然而,該術語也可以簡單代表批處理音頻文件、精靈、紋理文件和其他大數據集的行為。
因此,為了避免混淆,本專題提到的Unity中的批處理,通常指的是兩種用于批處理網格數據的主要機制:動態批處理和靜態批處理。這兩種方法本質上是幾何體合并的兩種不同形式,用于將多個對象的網格數據合并到一起,并在單一指令中渲染它們,而不是單獨準備和繪制每個幾何體。
將多個網格批處理為單個網格是可以實現的,因為沒有規定網格對象必須是3D空間中連續的幾何體。Rendering Pipeline(管線渲染)可以接受一系列沒有共同變的頂點,因此可以將本來需要多個渲染指令的獨立網格合并為單個網格,用單一指令渲染它。
多年來,關于動態批處理和靜態批處理系統的觸發條件,以及批處理在什么地方能夠帶來性能提升,一直存在許多困惑。畢竟,在某些情況下,如果沒有正確使用批處理,它的確會惡化性能。正確理解這些系統將有助于我們掌握顯著提升應用程序圖形性能所需的知識。
本文涵蓋如下專題:
- 管線渲染和Draw Call概念的簡單介紹
- Unity的材質和著色器如何一起工作,以渲染對象
- 使用Frame Debugger可視化渲染行為
- 動/靜態批處理的工作原理及優化方式
一、Draw Call
在單獨討論動態批處理和靜態批處理之前,首先要明白它們在管線渲染中試圖解決的問題。
這些批處理方法的主要目標是減少在當前視圖中渲染所有對象所需的Draw Call數量。就最基本的形式而言,Draw Call只是一個從CPU發送到GPU中用于繪制對象的請求。
注意:Draw Call是這一過程的通用行業術語,但在Unity中有時也稱為SetPass Call,因為一些底層方法也命名為SetPass Call。可以將Draw Call理解為初始化當前渲染過程之前的配置選項。本文剩余部分將其統稱為Draw Call。
在請求Draw Call之前,需要完成一些任務。首先,網格和紋理數據必須從CPU內存(RAM)送到GPU內存(VRAM)中,這通常發生在場景初始化期間,但僅限于場景文件知道的紋理和網格。如果使用非場景中的紋理和網格數據在運行時動態實例化對象,那么必須在它們實例化時完成加載。接著,CPU必須配置處理對象(這些對象就是Draw Call的目標)所需的選項和渲染特性,為GPU做好準備。
這些CPU和GPU間的通信任務是通過Graphics API進行的,這可以是DirectX、OpenGL等,取決于針對的平臺和指定的圖形設置。這些API調用通過一個稱為"驅動"的類庫來執行,該類庫包含一系列錯綜復雜的設置、狀態變量以及可以在應用程序中配置和執行的數據集(只是"驅動"庫旨在同時服務多個程序,以及來自多個線程的渲染器調用)。可用的特性會根據我們的顯卡和所針對的Graphics API發生巨大的變化;更高級的顯卡支持更高級的特性,但這需要由更新版本的API支持,因此需要更新的驅動程序來啟動它們。多年來創建的各種設置、支持的特性和版本之間的兼容性級別(特別是諸如OpenGL這樣的舊API),其數量簡直令人難以置信。幸運的事,在某種抽象級別上,所有這些API都傾向于以類似的方式運行,因此Unity可以通過一個公共接口支持很多不同的Graphics API。
在渲染對象之前,必須為準備管線渲染而配置的大量設置常常統稱為渲染狀態(Render State)。除非這些渲染狀態選項發生了變化,GPU將為所有傳入的對象保持相同的渲染狀態,并以類似的方式渲染它們。
更改渲染狀態是一個耗時的過程。例如,如果將渲染狀態設置為使用一個藍色紋理文件,然后要求它渲染一個巨大的網格,那么渲染會非常快,整個網格都顯示為藍色。然后,可以再渲染9個完全不同的網格,它們都顯示為藍色,因為沒有改變所使用的問題。然而,如果先用10種不同的紋理渲染10個網格,就將花費更長的時間。這是因為在每個網格發送Draw Call指令之前,需要使用新的紋理來準備渲染狀態。
用于渲染當前對象的紋理在Graphics API種實際上是一個全局變量,而在并行系統內修改全局變量說起來容易做起來難。在諸如GPU這樣的大規模并行系統中,實際上必須在修改渲染狀態之前一直等待,直到所有當前的作業打到同一個同步點為止(換句話說,最快的內核需要停下,等待最慢的內核趕上,這浪費了它們可以用于其他任務的時間),到達同步點后,需要重新啟動所有的并行作業。這會浪費很多時間,因此請求改變渲染狀態的次數越少,Graphics API越能更快遞處理請求。
可以出發渲染狀態同步的操作包括但不限于:立即推送一張新紋理到GPU,修改著色器、照明信息、陰影、透明度和其他任何圖形設置。
一旦配置了渲染狀態,CPU就必須決定繪制哪個網格,用什么紋理和著色器,以及基于對象的位置、旋轉和縮放(這些都在一個名為變換的4??4矩陣中表示,這正是Transform組件名字的由來)決定在何處繪制對象,然后發送指令到GPU以繪制它。為了使CPU和GPU之間的通信保持活躍,新指令被推入一個名為Command Buffer的隊列中。這個隊列包含CPU創建的指令,以及GPU每次執行完前面的指令后從中提取的指令。
批處理提升此過程性能的訣竅在于,新的Draw Call不一定意味著必須配置新的渲染狀態。如果兩個對象共享完全相同的渲染狀態信息,那么GPU可以立即開始渲染新對象,因為在最后一個對象完成渲染之后,還維護著相同的渲染狀態,這消除了由于同步渲染狀態而浪費的時間,也減少了需要推入Command Buffer中的指令數,減少了CPU和GPU上的工作負載。
二、材質和著色器
在Unity中,渲染狀態本質上是通過材質呈現給開發者的。材質是著色器的容器,著色器是一種用于定義GPU應該如何渲染輸入的頂點和紋理數據的簡短程序。著色器本身沒有必要的狀態信息來完成任何有價值的工作。著色器需要諸如漫反射紋理、法線影響和光照信息之類的輸入,并有效地規定了為了呈現傳入的數據需要設置哪些渲染狀態變量。
提示:著色器之所以如此命名是因為多年前,它們原本僅實現為處理對象的光照和著色(應用陰影,原本是沒有陰影的)。現在它們的功能已經有了巨大的增長,現在更通用的功能是作為訪問各種不同并行任務的可編程接口,但依舊使用之前的名字。
每個著色器都需要一個材質,而每個材質必須有一個著色器。甚至新導入場景中的網格,如果沒有賦予材質,就會自動被賦予默認(隱藏的)材質,為它們提供基本的漫反射著色器和白色色彩。因此,無法繞過這一關系。
提示:注意一個材質只支持一個著色器。要對一個網格使用多個著色器,需要將多個材質賦予該網格的不同部位。
所以,如果想要最小化渲染狀態修改的頻率,可以減少場景中使用的材質數量。這將同時提升兩個性能:CPU每幀花費更少的時間生成指令,并傳輸給GPU;而GPU不需要經常停止,重新同步狀態的變更。
為了理解材質和批處理的行為,先介紹一個簡單的場景。然而,在開始之前,應該僅用一些渲染選項,因為它們會產生一些額外的Draw Call,這可能會令人分散注意力:
1、導航到Edit|Project Settings|Quality,并設置Shadows為Disable Shadows(或者選擇默認的Fastest品質級別)。
2、導航到Edit|Project Settings|Player,打開Other Settings選項卡,并禁用Static Batching和Dynamic Batching(如果它們是開啟的)。
下一步創建一個場景,其中包含一個方向光、4個立方體和4個球體,每個對象都有獨特的材質、位置、旋轉和縮放。
然后看Game窗口的Stats彈出框中的Batching值共有9個批處理。該值嚴格等于渲染場景中使用的Draw Call數量。當前視圖將消耗其中一個批處理來渲染場景的背景,場景的背景可以設置為Skybox或Solid Clor,這取決于攝像機對象的Clear Flags設置。
剩余8個批處理用于繪制8個對象。對于每個對象,Draw Call需要使用材質的屬性準備管線渲染,并請求GPU根據對象當前的變換設置渲染給定的網格。給每個對象提供不同的紋理文件用于渲染,來確保材質是唯一的。因此,每個網格需要不同的渲染狀態,所以這8個不同網格都需要各不相同的Draw Call。
如前所述,理論上可以通過減少系統修改渲染狀態信息的頻率,來最小化Draw Call的數量。因此,一部分目標是減少使用的材質數。然而,如果所有對象都設置為使用相同的材質,則性能依舊沒有任何提升,批處理數量依然是9。這是因為渲染狀態變更的數量沒有真正減少,也沒有高效低合并網格信息。遺憾的是,管線渲染不夠智能,意識不到在重復寫入完全相同的渲染信息,并要求它一次又一次地渲染相同的風格。
三、Frame Debugger
在深入討論批處理之前,先研究一個有用的工具Frame Debugger,它有助于確定批處理是如何影響場景的。
要打開Frame Debugger,在主窗口中選擇Window|Frame Debugger或者在Profiler的Rendering區域中單擊BreakDown View Options中的Frame Debugger按鈕,這兩個操作都可以打開Frame Debug窗口。
單擊Frame Debug窗口的Enable按鈕,可以觀察場景是如何創建的,每次執行一個Draw Call。一般左側顯示GPU指令列表,右側顯示選中的Draw Call的詳細信息。
Frame Debugger窗口提供了很多有用的信息,可以用于調整單一Draw Call的行為,但最有用的區域是左邊面板的Drawing部分,其中列出了場景中的所有Draw Call,其中的每一項表示一個唯一的Draw Call和它渲染的對象。該工具的一個非常有用的特性是單擊其中任一項,就能立刻在Game窗口中看到場景渲染到所單擊記得那一項所需的Draw Call。這樣就可以快速、直觀地區分兩個連續的Draw Call,也很容易準確地指出給定的Draw Call渲染了哪些對象。可以通過查看在Draw Call期間出現了多少個對象,來幫助確定是否對一組對象進行批處理。
四、動態批處理
動態批處理有下面3個重要優勢:
- 在運行時生成
- 批處理中包含的對象在不同幀之間可能有所不同,這取決于哪些網格在主相機視圖中是可見的
- 能在場景中運動的對象也可以批處理
如果返回Player settings頁面并開啟Dynamic Batching,將看到批處理數量從9降到6。動態批處理自動識別共享材質和網格信息的對象,因此,將它們合并到一個大的批次中以供處理。還應該看到Frame Debugger中有一列不同的項,展示了正在進行動態批處理的網絡。
結果是,4個立方體合并到一個名為Dynamic Batch的Draw Call中,但4個球體依然通過4個獨立的Draw Call渲染,這是因為4個球體不滿足動態批處理的要求。盡管它們使用的材質相同,但還必須滿足很多其他條件。
對網格進行成功的動態批處理所需滿足的需求列表可以在Unity文檔中找到。
為給定網格執行動態批處理的要求如下:
- 所有網格實例必須使用相同的材質引用。
- 只有ParticleSystem和MeshRenderer組件進行動態批處理。SkinnedMeshRenderer組件(用于角色動畫)和所有其他可渲染的組件類型不能進行批處理。
- 每個網格至多有300個頂點,但是著色器使用的頂點屬性數不能大于900.這意味著對于復雜的著色器,每個網格的最大頂點數可能小于300。
- 對象不能在變換中包含鏡像(也就是說,一個具有正比例的游戲對象A和一個具有付比例的游戲對象B不能放在一起批處理)。
- 網格實例應該引用相同的光照紋理文件
- 材質的著色器不能依賴多個進程
- 網格實例不能接受實時投影
- 整個批處理中網格索引的總數有上線,這與所用的Graphics API和平臺有關,一般索引值為32~64。
重點關注術語"材質引用",因為如果使用兩個不同的材質,但他們的設置相同,則渲染管線的智能并不足以發現這一點,會把它們當成不同的材質,進而不執行動態批處理。其他要求已經解釋過了,然而,有幾個要求的描述并不直觀,需要額外解釋。
4.1頂點屬性
頂點屬性只是網格文件中基于每個頂點的一段信息,通常表示為一組浮點數。頂點屬性包括但不限于頂點位置(相對于網絡的根)、法線向量(一個從對抗表面指向外面的向量,通常用于光照計算)、一套或多套紋理UV坐標(用于定義一張或多張紋理如何包裹網格),甚至可能包括每個頂點的顏色信息(通常用于自定義光照或扁平化著色、低多邊形風格的對象)。只有著色器使用的頂點屬性總數小于900的網格才會進行動態批處理。
注意:查看網格的原始數據文件,其中包含的頂點屬性信息比Unity載入內存的少,這是由于引擎會將網格數據從原始數據格式轉化為內部格式。因此,不要假設3D建模工具提供的頂點屬性數量是最終的數量。驗證屬性數量的最好方式是將網格對象拖到場景中,在Project窗口中找到MeshFilter組件,在Inspector窗口的Preview子區域中查看verts值
在伴隨的著色器中,每個頂點使用的屬性數據越多,900個屬性預算就消耗得越多,從而減少網格允許擁有的頂點數量,這些頂點不能再用于動態批處理。例如,簡單的漫反射著色器只能給每個頂點使用3個屬性:位置、法線和一組UV坐標,因此,動態批處理可以使用這個著色器來支持總共有300個頂點的網格。然而,在更復雜的著色器中,每個頂點需要5個屬性,只能支持不超過180個頂點的網格的動態批處理。另外,請注意,即使咋著色器中每個頂點使用不到3個頂點屬性,動態批處理仍然只支持最多300個頂點的網格,因此只有相對簡單的對象才適合動態批處理。
這些限制證實場景開啟動態批處理之后,盡管所有對象共享相同的材質引用,也僅節省3個Draw Call的原因。Unity自動生成的立方體網格僅包含8個頂點,每個頂點都帶有位置、法線和UV數據,總共24個屬性,遠低于300個頂點和900個頂點屬性的上限。然而,自動生成的球體包含515個頂點,因此總共有1545個頂點屬性,明顯超過300個頂點和900個頂點屬性的限制,所以不能動態批處理。
如果單擊Frame Debuger中的一個Draw Call選項,就會顯示標簽為"Why this draw call can't be batched with previous one"的部分。大多數情況下,下方的解釋文本說明了哪個條件沒有被滿足(至少是它檢測到的首個條件),以及調試批處理行為的有用方法有什么。
4.2網格縮放
文檔清楚地建議,使用負數縮放會對動態批處理產生奇怪的效果。負數縮放通常是鏡像場景中網格的快速方式,可以避免創建和導入完全不同的網格,來生成僅沿著某個軸翻轉的對象。這個技巧通常用于創建一對門,或只是為了使場景看起來不同。然而,如果與沒有縮放或對兩個軸進行負數縮放的網格相比,只對1個軸或3個軸進行負數縮放的網格,會放到一個不同的動態批處理中。這與3個值(x,y,z)中哪個是負數無關,僅和負數值的數量是奇數或偶數有關。
在后臺,批處理分離行為的另一個奇怪的副產品是,對象的渲染順序可以決定什么網格能進行批處理。如果先前的對象出現在與當前對象不同的批處理組中,則無法對其進行批處理。同樣,最好舉例說明。再次假設5個對象:V以(1,1,1)縮放,W以(-1,1,1)縮放,X以(-1,-1,1)縮放,Y以(-1,-1,-1)縮放,Z和V均以(1,1,1)縮放。對象V和Z使用相同的等比縮放,因此它們會被批處理到一起。然而,如果以上述順序渲染所有對象到場景中,那么V會被渲染,接著Unity測試對象W和V是否可以批處理到一起。由于W有及數個附屬縮放,因此不能和V進行批處理。Unity接著比較X和W,檢查它們是否可以批處理到一起,依然不行,因為W有及數個附屬縮放,而X有偶數個。然后比較對象W-Y和Y-Z,失敗的原因都相同,最終結果是5個對象用5個Draw Call渲染,沒有機會進行V和Z的批處理合并。注意,只有使用負數縮放,才會產生這個奇怪的效果。
據推測,這是用于檢測有效可批處理組的算法的唯一副產品,由于在兩個維度上鏡像網格,在數學上等價于網格繞相同的軸旋轉180°,而沒有哪種旋轉等價于網格沿著1個軸或3個軸進行鏡像,因此所觀察到的行為可能只是動態批處理系統自動轉換了對象,盡管這并不完全清楚。無論如何,希望這能為生成動態批處理時可能遇到的許多奇怪情況做好準備。
4.3動態批處理總結
渲染大量的簡單網格時,動態批處理是非常有用的工具。使用大量外觀幾乎相同的簡單物體時,該系統的設計是非常完美的。應用動態批處理的可能情況如下:
- 到處都是石頭和樹木的森林
- 有很多簡單而常見的元素(計算機、走廊、管道等)的建筑、工廠或空間站
- 一個游戲包含很多動態的非動畫對象,還包含簡單的幾何體和粒子特效(如幾何戰爭等游戲)。
阻止兩個對象動態批處理的唯一條件是,它們使用了不同的紋理,就應該花點時間和精力合并紋理(通常稱為圖集),并重新生成網格UV,以便進行動態批處理。這可能會犧牲紋理的質量,或者紋理文件會變大,但這是值得的。
動態批處理可能對性能造成損害的唯一情況是,設置一個場景,其中有數百個簡單對象,而每個批處理中只有幾個對象。在這種情況下,檢測和生成這么多小批處理組的開銷成本可能比為每個網格單獨執行Draw Call所節省的時間還要多。即便如此,一般也不會發生這種情況。
簡單地假設正在進行動態批處理,則更有可能給應用程序帶來性能損失,而實際上我們忘記了其中一個必要條件。推送一個新的網格版本,可以意外地突破定點限制,在Unity將一個原始對象(擴展名為.obj)轉換成它自己的內部格式的過程中,生成的頂點屬性比預期的要多。要突破定點限制,還可以調整一些著色器代碼,或添加額外的過程,但不會取消對象進行動態批處理的資格,甚至可以設置對象來啟用陰影或光線探測,但這會破壞另一個條件。
當這些意外發生時,并沒有發出警告,只是指出修改后Draw Call的數量在增加,性能也進一步下降了。為了使場景中動態批處理的數量保持合適的水平,需要連續不斷地檢查Draw Call的數量,并觀察Frame Debugger的數據,以確保最新的修改不會意外取消對象的動態批處理資格。然而,與往常一樣,如果證實這會造成性能瓶頸,那么僅需關心Draw Call的性能。
總之,每種情況都是各不相同的,需要使用網格數據、材質和著色器進行實驗,以確定能動態批處理什么,不能動態批處理什么,并對場景不時地執行一些測試,以確保使用數量合理的Draw Call。
五、靜態批處理
Unity通過靜態批處理提供了第二種批處理機制。靜態批處理功能在幾個方面類似于動態批處理,例如對哪些對象進行批處理,取決于運行時它對攝像機是否可見,批處理的內容每幀都不同。然而,靜態批處理只處理標記為Static的對象,因此命名為靜態批處理。
靜態批處理系統有自己的要求:
- 網格必須標記為Static(具體來說是Batching Static)
- 每個被靜態批處理的網格都需要額外的內存
- 合并到靜態批處理中的頂點數量是有上限的,并隨著Graphics API和平臺的不同而不同,一般為32~64K個頂點
- 網格實例可以來自任何網格數據源,但它們必須使用相同的材質引用。
接下來細述這些要求
5.1Static標記
靜態批處理只能應用于開啟Static標記的對象,具體而言是Batching Static子標記(這些子標記稱為StaticEditorFlags)。單擊GameObject的Static選項旁邊的下三角按鈕,會出現一個StaticEditorFlags下拉列表框,該框可以為不同的Static處理過程修改對象的行為。
Static標記的一個明顯的副作用是不能修改對象的變換。因此,任何想要使用靜態批處理的對象都不能通過任何方式移動、旋轉和縮放。
5.2內存需求
靜態批處理的額外內存需求取決于批處理的網格中復制的次數。靜態批處理在工作時,將所有標記為Static的可見網格數據復制到一個更大的網格數據緩沖中,并通過一個Draw Call傳到管線渲染中,同時忽略原始網格。如果所有進行靜態批處理的網格都各不相同,那么與正常渲染對象相比,這不會增加內存使用量,因為存儲網格需要的內存空間量是相同的。
然而,由于數據是高效復制過來的,因此這些靜態批處理的副本會消耗額外的內存,其數量等于網格的數量乘以原始網格的大小。通常,渲染1個、10個或100萬個相同的對象,消耗的內存是相同的,因為它們都引用相同的網格數據。在這種情況下,對象之間的唯一區別就是每個對象的變換。然而,因為靜態批處理需要把數據復制到一個大的緩沖區,所以這個引用會丟失,原因是原始網格的每個副本都會復制到緩沖區中,每個副本都帶著各不相同的數據集,以及附著到頂點位置的硬編碼變換。
因此,使用靜態批處理渲染1000個相同的樹對象,消耗的內存是不使用靜態批處理渲染相同樹的1000倍。如果沒有正確地使用靜態批處理,將導致一些嚴重的內存消耗和性能問題。
5.3材質引用
如前所述,共享材質引用時間少渲染狀態變更的一種方式,因此該要求顯而易見。另外,有時靜態批處理需要更多材質的網格。在這種情況下,所有網格會根據所使用的材質劃分到各自的靜態批處理組,每個組使用不同的材質。
該要求的缺點是,靜態批處理渲染所有靜態網格時,使用的Draw Call數量最多只能等于所需的材質數量。
5.4靜態批處理的警告
靜態批處理有幾個缺點。它實現批處理的方式是將網格合并到一個更大的網格中,所以靜態批處理系統有一些需要注意的警告。這些警告包括較小的不便和明顯的缺點,這取決于場景:
- Draw Call減少了,但不能直接在Stats窗口中看到,要在運行時才能看到
- 在運行時向場景中引入標記為Batching Static的對象,不能自動包含到靜態批處理中
下面深入討論這些問題
5.4.1靜態批處理的Edit模式調試
試圖確定靜態批處理在場景中的整體效果有一些困難,因為在Edit模式下,靜態批處理沒有生效,因此在手動測試之前,難以確定靜態批處理提供了什么優勢。應該用Frame Debugger來驗證靜態批處理是否正確生成,以及是否包含了預期的對象。
如果在項目后期才開始啟用該特性,可能會有問題,因為此時需要花費大量時間啟動、調整、重啟場景,以確保節省了期待節省的Draw Call。所以,最好在構建新場景的早起開始進行靜態批處理優化。
不言而喻,靜態批處理創建工作并不完全是瑣碎的,如果有許多批處理要創建,或有許多大型對象要批處理,那么場景初始化時間可能會顯著增加。
5.4.2在運行時實例化靜態網格
在運行時添加到場景中的任何新對象,即使它們標記為Batching Static對象,也不會由靜態批處理系統自動合并到任何現有批處理中。自動合并會導致重新計算網格和與管線渲染同步時造成巨大的運行時開銷,所以Unity甚至不會嘗試自動合并。
大多數情況下,應該嘗試讓任何期望被靜態批處理的網格出現在場景的原始文件中。然而,如果需要動態實例化,或者使用疊加方式加載場景,就可以使用StaticBatchUtility.Combine()方法控制靜態批處理。該工具方法有兩個重載形式:一個重載形式需要提供根GameObject,該對象中所有帶網格的子GameObject對象都會轉換到新的靜態批處理組中(如果使用了多個材質,就會創建多個組);另一種重載形式需要提供GameObject列表和一個根GameObject,該重載形式會自動將列表中的對象作為根對象的子節點,以相同的方式生成新的靜態批處理組。
應該分析一下StaticBatchUtility.Combine()的用法,因為如果有許多頂點要合并,那么該操作的開銷將非常大。它也不會將給定的網格與任何預先存在的靜態批處理組合并在一起,即使它們使用相同的材質。因此,無法通過實例化或疊加加載的靜態網格來減少Draw Call,這些靜態網格使用的材質與場景中已經存在的其他靜態批處理組相同(它只能與在Combine()調用中分組的網格合并)。
提示:如果調用StaticBatchUtility.Combine()方法進行批處理之前,GameObject沒有被標記為Static,GameObject就一直是非Static,但網格自身是Static。這意味著GameObject、它的Colider組件和其他任何重要對象可能被意外移動,但網格依然留在原處。對于在靜態批處理的對象中意外混合Static和非Static狀態,要特別小心
5.5靜態批處理總結
靜態批處理是一種強大但危險的工具,如果使用不當,就很容易因為呢誒村小號和應用程序的渲染成本造成巨大的性能損失。它還需要大量的手動調整和配置,以確保正確生成批處理,不會由于使用各種Static標記而意外引發一些不期望的負面效果。它有一個顯著的優勢,可以用于不同形狀和巨大尺寸的網格,這是動態批處理無法實現的。