標籤:圖集 透明 文章 title 需要 _for file rdsh --
本文是Unity官方教程,效能最佳化系列的第四篇《Optimizing graphics rendering in Unity games》的翻譯。
相關文章:
Unity效能最佳化(1)-官方教程The Profiler window翻譯
Unity效能最佳化(2)-官方教程Diagnosing performance problems using the Profiler window翻譯
Unity效能最佳化(3)-官方教程Optimizing garbage collection in Unity games翻譯
Unity效能最佳化(4)-官方教程Optimizing graphics rendering in Unity games翻譯
簡介
本文我們將會學習在Unity渲染一幀的幕後會發生什麼,渲染時會發生哪種類型的效能問題,以及怎樣去解決和渲染相關的效能問題。
閱讀本文之前,理解沒有一刀切的方法去改進渲染效能問題是至關重要的。渲染效能被我們遊戲中的很多因素影響並且高度依賴我們遊戲啟動並執行硬體和作業系統。最重要的是我們通過調查,實驗,以及精確分析效能檢測的結果來解決效能問題。
本文包括了一些最常見的渲染效能問題的資訊,以及解決方案和一些擴充閱讀資源的連結。很可能我們的遊戲有一些問題本文沒有覆蓋。儘管如此,本文仍然會協助我們理解我們的問題,給我們一些基礎知識使得我們更有效去尋找解決方案。
渲染的簡要介紹
開始之前,我們快速簡要的看看Unity渲染一幀時發生了什麼。理解事件流和事情發生的正確時期將會協助我們理解,研究,並且努力解決我們的效能問題。
備忘:在本文中,我們將使用“object”,指代我們遊戲中被渲染的對象。任何帶著渲染組件的GameObject都講稱作object。
基本上,渲染流程如下:
-CPU計算出什麼東西需要被繪製,並且怎麼被繪製。
-CPU給GPU發送指令。
-GPU根據CPU的指令進行繪製。
現在讓我們進一步看看發生了什麼。在本文的後面,我們將覆蓋上面每個步驟的細節。但是現在,我們先熟悉一些將會使用的名詞,以及理解在渲染中CPU和GPU所扮演的不同角色。
渲染的過程通常被稱為渲染管線,這十分有助於記憶。高效的渲染在於保持資訊流動。
在渲染每一幀的時候,CPU都會做如下工作:
-CPU檢查螢幕上的每個object,決定他們是否應該被渲染。一個object只有滿足了一定的條件才會被渲染。例如,它的一部分碰撞盒必須要在攝像機的視錐體內。不被渲染的object稱作被剔除了。想瞭解更多關於視錐體和視錐體剔除的資訊,請閱讀this page。
-CPU收集將要被渲染的每個object的資訊,並把這些資料分類為指令被稱作繪製指令draw calls。一個draw call包含網格資料以及網格應該怎樣被渲染。例如,應該使用哪個紋理。在一定的情況下,共用設定的一些objects可能會被合并為一個draw call。合并不同objects的資料到同一個draw call被稱作batching。
-CPU給每個draw call建立一個資料包,稱為batch。Batch有時會包含一些draw call以外的資料,但是這些情況通常不會對效能有什麼影響,因此在本文中我們將不會考慮這些資料。
每一個batch必須包含一個draw call,CPU現在會做如下工作:
-CPU可能會發出指令,使得GPU改變一些渲染狀態的變數。這個指令被稱為SetPass call。SetPass通知GPU,使用哪些設定去渲染下一個網格。只有在渲染下一個網格時,渲染狀態和渲染上一個網格時發生了改變的情況下,才會調用SetPass call。
-CPU把draw call發送給GPU。draw call指示GPU使用最近的SetPass call定義的設定去渲染指定的網格。
-一定的情況下,batch可能需要不止一個的pass。pass是一段shader代碼,並且新的pass需要改變渲染狀態。對於batch中的每個pass,CPU必鬚髮送一個新的SetPass call然後必須要再次發送draw call。
同時,GPU做如下工作:
-GPU按照CPU發送的指令順序處理這些指令。
-如果當前任務是SetPass call,那麼GPU更新渲染狀態。
-如果當前任務是draw call,那麼GPU渲染網格。渲染網格發生在很多階段,不同階段的shader代碼可以定義渲染。這部分的渲染過程十分複雜,我們不會詳細講解,但是理解下面的知識是很有用的:頂點著色器vertex shader告訴GPU怎麼處理網格的頂點。片元著色器fragment shader告訴GPU怎麼繪製單獨的像素。
-這個過程會重複執行,知道所有CPU發送的任務都被GPU完成。
現在我們瞭解了Unity渲染幀時發生了什麼,讓我們考慮渲染時可能發生的一些問題。
渲染問題的類型
理解渲染最重要的是:為了渲染一幀,CPU和GPU必須都完成他們的任務。他們中的任何一個花費了太長的時間去完成任務,都會造成渲染延遲。渲染問題有兩個基本的原因。第一類問題是由低效的渲染管線引起的。當渲染管線中一步或者多步花費了太長時間,打斷了平滑的資料流時,渲染管線會很低效。渲染管線的低效被稱為瓶頸。第二類問題是由於,渲染管線被塞入了太多的資料。即使是最高效的渲染管線,對於一幀中可以處理的資料量也是有限制的。
當我們的遊戲花費太長的時間去渲染一幀,是由於CPU花費了過長的時間去完成渲染任務時,我們的遊戲被稱為CPU約束。當我們的遊戲花費太長的時間去渲染一幀,是由於GPU花費了過長的時間去完成渲染任務時,我們的遊戲被稱為GPU約束。
理解渲染問題
在我們做任何修改前,先使用Profiler理解引起問題的原因是至關重要的。不同的問題需要不同的解決方案。測量我們做出的改變的效果也是很重要的。修複效能問題是一項平衡性的工作,改進一方面的效能問題,很可能會對其他方面有負面影響。
我們將使用兩個工具來協助我們理解和解決渲染效能問題。Profiler和frame debugger。他們都是Unity內建的工具。
Profiler
Profiler視窗允許我們即時的看到遊戲的效能,我們可以看到遊戲的很多方面的資料,包括記憶體使用量,渲染管線,指令碼效能等等。
如果你還不熟悉使用Profiler,請參看Unity使用者手冊this page of the Unity Manual,以及這篇教程Unity效能最佳化(1)-官方教程The Profiler window翻譯 。
Frame Debugger
frame debugger允許我們一步一步的查看一幀是怎樣被渲染的。我們可以使用它查看詳細的渲染資訊,如在每個draw call中都繪製了什麼,每個draw call的shader屬性以及發送到GPU的事件的順序。這些資訊協助我們理解我們的遊戲是怎樣渲染的以及我們可以在哪裡改進效能。
如果你還不熟悉frame debugger的使用,請參考this page of the Unity Manual以及這個視頻教程this tutorial video。
找到效能問題的原因
在改進我們遊戲的渲染效能前,我們必須先確認我們遊戲運行慢是由渲染問題引起的。如果真正的問題是由於過於複雜的使用者指令碼引起的,那麼我們去最佳化渲染效能是毫無意義的。如果你不太確認,效能問題是否有渲染引起的,請參考Unity效能最佳化(2)-官方教程Diagnosing performance problems using the Profiler window翻譯 。
一旦我們確認是渲染問題,我們必須同時確認是CPU限制還是GPU限制。不同問題有不同的解決方案,所以在試圖修複問題前,理解引起問題的原因是至關重要的。如果你不確定你的遊戲是CPU限制還是GPU限制,請參考Unity效能最佳化(2)-官方教程Diagnosing performance problems using the Profiler window翻譯 。
如果是CPU受限
基本上,在渲染一幀中,CPU的工作分為三類:
-決定什麼必須被繪製
-為GPU準備好命令
-發送命令給GPU
這三類工作包含很多獨立的任務,這些任務可能是通過多線程工作的。多線程允許不同的任務同時執行;當一個線程執行一個任務時,另一個線程可以同時執行完全不同的任務。這意味著工作能夠更快的完成。當渲染任務被分發到不同的線程時,稱為多線程渲染。
Unity渲染過程中和三類線程相關:主線程,渲染線程和工人線程(the main thread, the render thread and worker threads)。主線程用於我們遊戲的主要CPU任務,包括一些渲染任務。渲染進程是專門用於發送命令給GPU的。每個工人線程執行一個單獨的任務,例如剔除和網格蒙皮。哪些任務執行在哪個線程,取決於我們遊戲啟動並執行硬體和遊戲的設定。例如,CPU的核心數量越多,就會產生越多的工人線程數。由於這個原因,在我們的目標硬體上進行效能分析是十分重要的。在不同的裝置上,我們遊戲的表現可能相差很多。
由於多線程渲染非常複雜並且依賴硬體,在我們嘗試改進效能時,必須首先理解是哪些任務導致了CPU限制。如果我們的遊戲運行緩慢是因為在一個線程上剔除操作花費了太長的時間,那麼我們在另一個線程上降低發送給GPU命令的時間是不會有什麼協助的。
注意:不是所有的平台都支援多線程渲染,在此時,WebGL不支援這個功能。在不支援多線程渲染的平台上,所有的任務都在相同的線程中執行。如果在這樣的平台上,CPU限制時,任何最佳化CPU的工作都會改進CPU效能。如果我們的遊戲是這種情況,我們應該閱讀下面的全部章節,並且考慮哪些最佳化最適合我們的遊戲。
圖形工作Graphics jobs
在Player settings中的Graphics jobs選項決定了Unity是否使用工人線程去執行一些原本需要在主線程或者渲染線程中執行的任務,在支援這個功能的平台上,它能夠提供可觀的效能提升。如果我們希望使用這個功能,我們應該分別對開啟或者關閉此功能進行效能分析,以觀察這個功能對效能的提升效果。
找出哪些任務對效能問題有影響
我們可以使用Profiler來檢查哪些任務引起了CPU限制。請參考。。。
現在我們理解了哪些任務引起我們遊戲CPU限制,讓我們看看常見的問題和解決方案。
發送命令到GPU
發送命令到GPU花費時間過長是引起CPU限制的最常見的原因。在大多數平台上,這個任務是由渲染線程執行的,個別平台在工人線程上執行(如ps4)。
發送命令到GPU中,其中最耗時的操作是SetPass call。如果CPU限制是由發送命令到GPU引起的,那麼降低SetPass的數量通常是最好的改善效能的辦法。
在Unity的渲染Profiler中,我們可以看到有多少SetPass call和batches被發送。有多少SetPass call被發送會造成效能問題,和遊戲的目標硬體關係很大。在高端pc上可以發送的SetPass call數量遠遠大於移動平台。
SetPass call以及相關的batches數量取決於幾個因素。本文稍後會詳細闡述。簡單來說,通常如下:
-降低batches數量或者使更多的對象共用相同的渲染狀態,通常會減低SetPass call數量。
-降低SetPass call數量,通常會改進CPU效能。
如果降低batches數量沒有降低SetPass call數量,他本身也會導致效能改進。這是因為CPU能夠更有效率的處理單個batch,即使它和幾個batches包含了數量相同的網格資料。
基本上,有三種方法降低SetPass call和batches的數量,我們將仔細論述下面的每一種方法:
-降低要渲染的對象數量,通常可以同時降低SetPass call和batches的數量。
-降低每個要渲染的對象的渲染次數,通常可以降低SetPass call
-合并要渲染的對象的資料到更少的batches,可以降低batches數量
不同的技術適用於不同的遊戲,我們將考慮以上所有的選擇,決定哪些適合我們的遊戲並且做出實驗。
降低要渲染的對象數量
降低要渲染的對象數量是最簡單的降低batches 和 SetPass calls的方法。有以下幾個技術,可以降低要渲染的對象數量:
-簡單的降低我們情境的可見對象數量,是一個有效解決方案。例如,我們要渲染有很多人物的人群,我們可以嘗試減少人物的數量,如果看起來人群的效果仍然不錯,那麼這就是一個比其他複雜方法快捷的多的最佳化方法。
-我們可以通過設定攝像機的剪裁平面的遠端來降低攝像機的繪製範圍。這個屬性工作表示距離攝像機多遠的物體將不再被渲染。如果我們想隱藏遠處的物體不被渲染的事實,我們可以嘗試使用霧來掩蓋遠處。
-如果需要基於距離的更細粒度的隱藏物體,我們可以使用攝像機的Layer Cull Distances屬性,它可以給不同的Layer設定單獨的剪裁距離。如果我們有很多前景裝飾細節時,這個方法很有用。我們可以使用很小的距離隱藏細節。
-我們可以使用遮擋剔除功能去關閉被其他物體遮擋的物體的渲染。例如,我們情境中有一個很大的建築,我們可以使用遮擋剔除功能,關閉它後面的物體的渲染。Unity的遮擋剔除功能不適用於所有的情境,它會導致額外的cpu消耗,並且相關設定很複雜,但是在一些情境中,它可能會極大的改善效能。使用遮擋剔除的最佳實務,This Unity blog post on occlusion culling best practices。
另外,我們可以通過手工的關閉物體渲染來實現我們自己的遮擋剔除,我們可以手工的關閉我們知道玩家無法看到的物體的渲染。例如,如果我們的情境包含一些過場的物體,那麼在他們出現之前或者移出以後,我們應該手工的關閉他們的渲染。對於我們遊戲來說,使用我們的知識,手工的剔除,往往比Unity動態遮擋剔除有效多。
降低每個要渲染的對象的渲染次數
即時的光照,陰影和反射可以極大的提高我們遊戲的真實感,但是這些操作可能非常昂貴。使用這些功能可能導致物體被渲染多次,這對效能有很大的影響。
這些功能的精確的影響依賴於我們遊戲選擇的渲染路徑。渲染路徑,也就是表明當我們繪製情境的時候,渲染計算的執行順序。不同渲染路徑最主要的不同,是它們怎麼處理即時光照,陰影和反射。通常來說,如果我們遊戲運行在比較高端的裝置上,並且應用了很多即時光照,陰影和反射時,延遲渲染是比較好的選擇。前向渲染適用於低端裝置,並且不使用以上功能。儘管如此,如果我們需要更好的使用即時光照,陰影和反射等功能,情況是十分複雜的,最好研究相關主題以及實驗。請參考This page of the Unity Manua這是一個十分有用的起點。 請參考This tutorial這裡包含了Unity中光照的相關主題內容。
不論選擇哪種渲染路徑,使用即時光照,陰影和反射都會影響我們遊戲的效能,所以,理解怎樣去最佳化他們是十分重要的。
-Unity中動態光照是很複雜的主題,討論他超過了本文範圍,請參考this tutorial和 this page of the Unity Manual。
-動態光照很昂貴。當我們的情境包含靜態物體時,例如風景,我們可以使用烘焙技術去預計算情境的光照,這樣就不需要在運行時計算光照了。具體請參考This tutorial和 this section of the Unity Manual。
-如果我們希望在遊戲中使用即時陰影,我們可能可以改進這方面的效能。This page of the Unity Manual 這篇文章介紹的陰影設定,以及這些設定怎麼影響效能的。例如,我們可以設定陰影距離,確保只有近處的物體投射陰影。
-反射探頭建立真實感的反射,但是會很大的影響batches數量。最好我們在效能敏感的場合最小化的使用它,並且無論在哪使用了他們都要儘可能的最佳化。反射探頭的最佳化請參考This page of the Unity Manual。
合并物體為更少的batches
在一定情況下,一個batch可以包含多個物體的資料。為了可以適合合并,物體必須滿足以下條件:
-共用相同材質的相同執行個體
-有一樣的設定(例如,紋理,shader,shader參數等)
合并合適的物體可以改進效能,儘管如此,所有的最佳化技術我們都必須小心的分析,合并的消耗沒有超過獲得的效能改進。
合并合適的物體有幾種不同的技術:
-靜態batching技術,允許Unity合并相鄰的不移動的合適的物體。一個好的例子是,一堆相似的物體,例如巨石,可以從靜態batch中受益良多。在遊戲中設定靜態batch,請參考This page of the Unity Manual。
靜態batch會導致更高的記憶體佔用,所以我們在最佳化時要衡量這個代價。
-動態batching技術,是另外一種Unity合并合適的物體的技術,不論它是運動還是靜止。對能夠使用這種技術合并的物體,有一些限制。這些限制請參考this page of the Unity Manual 動態batching會影響CPU使用,可能會引起CPU消耗的時間大於節省的時間。我們應該通過實踐衡量它的代價,並且在使用的時候注意這些。
-合并Unity的UI元素要更複雜一些,因為他會受我們介面布局的影響。具體請參考This video from Unite Bangkok 2015和this guide to optimizing Unity UI。
-GPU instancing技術允許大量一樣的物體十分高效的合并處理。它的使用有一定限制,並且不是所有硬體都支援,但是如果我們的遊戲在情境中同時存在大量相同的物體,我們可以在這個技術下收益。請參考This page of the Unity Manual 這裡介紹了Unity中GPU instancing的技術細節以及怎樣使用它,支援哪些平台,以及在哪種環境下,我們的遊戲會受益於此。
-紋理圖集,是把大量的小紋理合并為一張大的紋理圖的技術。它通常在2d遊戲和UI系統中使用,但是也可以在3d遊戲中使用。當我們使用這個技術為遊戲建立美術資源時,我們可以確保物體共用同一圖集,因此適合合并。Unity內建了圖集工具Sprite Packer。
-我們可以手工的合并共用相同材質和紋理的網格,不論是在Unity編輯器中還是在運行時使用代碼。當我們手工合并網格時,我們必須意識到,陰影,光照和剔除仍然在每個單獨的物體層級上操作。這意味著,合并網格所產生的效能提升,很可能被本來可以被剔除的物體,不再被剔除了,帶來的影響抵消。如果我們想深入研究這項技術,我們應該查看Mesh.CombineMeshes函數 ,Unity’s Standard Assets package中的CombineChildren指令碼是一個例子。
-在指令碼中,我們必須小心使用Renderer.material,這會複製材質,並且返回一個新副本的引用。這樣做會破壞batching,如果這是合并中的一部分。因為renderer不再持有相同的材質引用了。如果我們需要訪問一個在合并中的物體的材質,我們應該使用Renderer.sharedMaterial。
剔除,排序以及合并
剔除,收集將要被繪製的物體的資料,排序這些資料,產生GPU命令,這些都對CPU限制有貢獻。這些任務會在主線程或者獨立的工人線程中運行,取決於我們遊戲的設定以及目標硬體。
-剔除本身消耗並不大,但是減少不必要的剔除操作可能會對效能有協助。剔除是對情境中每個啟用的物體,每個攝像機每個物體的計算,甚至是哪些不被渲染的層級的物體。為了降低這些,我們應該關閉攝像機,並且對於當前不使用的物體反啟用或者禁用renderer。
-Batching可以極大的增加向GPU發送命令的速度,但是他有時可能會在其他地方帶來消耗。如果batching操作造成了CPU限制,我們可以會要限制手工或者自動batching操作的數量。
蒙皮
SkinnedMeshRenderers在當我們使用一個網格動畫變形時(技術上稱為骨骼動畫)使用。它最多的用在動畫人物身上。渲染蒙皮的任務通常在主線程或者單獨的工人線程,依賴於遊戲的設定以及目標硬體。
渲染蒙皮可能會是昂貴的操作。如果我們在渲染Profiler中,看到渲染蒙皮對CPU限制影響很大,這裡有幾個方法我們可以嘗試去改進它的效能。
-我們應該考慮當前正在使用SkinnedMeshRenderers組件的物體是否有必要使用。可能是這種情況,我們匯入的模型包含了SkinnedMeshRenderers組件,但是我們其實並不需要它運動。這種情況下,我們使用MeshRenderer組件替換它,會有助於效能提升。當我們在Unity中匯入模型時,如果我們在模型的匯入設定中選擇不匯出動畫,這個模型就會包含一個MeshRenderer組件替換SkinnedMeshRenderers組件。
-如果我們只在一些時刻運動物體(例如,只在遊戲開始時,或者只有距離攝像機一定距離內時),我們應該交換為一個細節較少的網格,或者使用MeshRenderer替換SkinnedMeshRenderers。SkinnedMeshRenderers組件有一個函數BakeMesh,可以用匹配的動作建立一個網格,這個十分有用,在不同的網格或渲染器中切換時物體不會有可見的變化。
-這些文章有關於使用蒙皮的最佳化建議,請參考This page of the Unity Manual 和the Unity Manual page on the SkinnedMeshRenderer component,還應該記住,蒙皮的消耗是在每個頂點上,因此,使用頂點較少的模型可以有效降低工作量。
-在一些平台上,和CPU相比,蒙皮可以被GPU更高效的處理。如果我們的GPU比較強,這個值得嘗試。我們可以為當前平台開啟GPU蒙皮,在Player Settings中。
主線程操作和渲染無關
理解許多主線程的任務是和渲染無關的很重要。這意味著,如果是CPU限制發生在主線程,我們應該把最佳化CPU時間改進效能的努力放在和渲染無關的任務上。
例如,在某個時間點上,我們的遊戲需要做十分昂貴的渲染操作並且在主線程上我們的指令碼操作也非常昂貴,使得CPU限制。如果我們已經在不損失視覺真實度的前提下儘可能的最佳化渲染了,那麼我們減少使用者指令碼的CPU消耗可能會改進效能。
如果遊戲是GPU限制
如果遊戲是GPU限制,那麼第一件事就是找到GPU瓶頸的原因。GPU效能最常被填充率限制,尤其在移動平台,但是顯存頻寬和頂點處理也可能影響。讓我們檢查這些問題,並且學習引起問題的原因,怎麼診斷和怎麼修複問題。
填充率
填充率是指GPU在螢幕上每秒可以渲染的像素數。如果我們的遊戲收到填充率的限制,意味著我們的遊戲每幀嘗試繪製的像素數量超過了GPU的處理能力。
檢查是否填充率引起了遊戲GPU限制很簡單:
-使用Profiler分析,注意GPU時間
-在Player settings中降低顯示解析度
-再次分析遊戲,如果效能改善了,很可能是填充率的問題
如果確認了填充率引起了問題,有幾個方法可以解決問題:
-片元shader是告訴GPU怎麼樣去繪製一個像素的一段shader代碼。這段代碼GPU需要為每一個需要繪製的像素執行,所以如果這段代碼效率低,那麼很容易發生效能問題。複雜的片元shader是很常見的引起填充率問題的原因。
-如果我們的遊戲使用Unity內建shader,我們應該使用最簡單和最佳化的shader,為了達到我們想要的視覺效率。例如,the mobile shaders是Unity針對移動平台高度最佳化的shader,我們應該實驗使用它們是否可以在不影響視覺效果的前提下改善效能。這些shader是為了移動平台設計的,但是它們也適用於任何項目。如果使用它們可以達到項目視覺效果的要求,那麼在非移動平台上使用它們也是能夠很好的改善效能的。
-如果遊戲中的物體使用的是Unity的Standard Shader,那麼理解Unity編譯這些shader是基於當前的材質設定的是很重要的。只有那些當前使用的功能會被編譯。這意味著,移除例如detail maps可以減少片元shader的複雜度,這對效能提升有很大益處。如果我們遊戲中是這種情況,我們應該實踐,是否能夠在不影響視覺品質的前提是提升效能。
-如果我們遊戲使用的是定製的shader,我們應該儘可能的最佳化它。最佳化shader是一個很複雜的主題,請參考this page of the Unity Manual和 this page of the Unity Manual。
-Overdraw是指相同的像素繪製了多次。這是在物體繪製在其他物體之上的時候發生的,也在很大程度上引起了填充率問題。為了理解Overdraw,我們必須先理解Unity在情境中繪製物體的順序。物體的shader決定了物體的繪製順序,通常由render queue屬性決定。Unity使用這些資訊按照嚴格的順序繪製物體,具體細節請參考page of the Unity Manual 另外在不同render queue的物體在被繪製之前會按不同的順序排序。例如,Unity在Geometry queue中為最小化Overdraw會從前到後排序物體,但是在Transparent queue中,為了達到視覺效果的要求,則是從後到前排序物體。在Transparent queue中,從後向前排序物體其實最大化了Overdraw。Overdraw是一個很複雜的主題,並且沒有一刀切的解決方案,但是降低重疊物體的數量使得Unity不能自動排序是關鍵。調查Overdraw問題最好的起點是Unity的情境視圖中,DrawMode允許我們看到情境中的Overdraw,我們可以從這開始降低Overdraw的工作。最常見的引起Overdraw的因素是透明材質,未最佳化的粒子,和重疊的UI元素。所以我們應該嘗試最佳化這些。請參考This article on the Unity Learn site 這篇文章聚焦於UI,但是也包含了Overdraw的很好的指導。
-使用螢幕後處理技術也會極大的影響填充率,尤其是我們使用了不止一種的螢幕後處理的時候。如果我們在使用螢幕後處理是遇到了填充率問題,我們應該嘗試不同的設定或者使用更加最佳化的螢幕後處理版本。例如使用Bloom (Optimized)替換Bloom。如果我們在同一個攝像機下使用了多個螢幕後處理,這將造成成倍的shader pass。這種情況下,我們應該合并shader到一個單獨的pass,例如Unity’s PostProcessing Stack。如果我們最佳化螢幕後處理效果後,仍然有填充率問題,那麼我們也許要考慮關閉螢幕後處理,尤其是在低端的裝置上。
顯存頻寬
顯存頻寬是指GPU讀寫它的專用記憶體的速度。如果我們的遊戲受限於顯存頻寬,通常意味著我們使用的紋理太大了,以至於GPU無法快速處理。
我們可以按如下方法檢查是否顯存頻寬的問題:
-用Profiler分析遊戲,並關注GPU時間
-在品質設定中降低當前平台的紋理品質
-繼續分析遊戲,如果效能改善了,那麼通常是顯存頻寬的問題。
如果是顯存頻寬的問題,我們需要降低紋理的記憶體佔用。針對不同的遊戲通常有不同的最佳解決方案,這裡我們提供幾個最佳化紋理的方法:
-紋理壓縮技術可以同時極大的降低紋理在磁碟和記憶體中的大小。如果是顯存頻寬的問題,那麼使用紋理壓縮減小紋理在記憶體的大小可以協助改善效能。Unity中有很多可用的紋理壓縮的格式和設定。通常來說,一些紋理壓縮格式只要可用就應該儘可能的使用,儘管如此,通過實踐找到針對每個紋理最合適的設定是最好的。請參考This page in the Unity Manual 講述了紋理壓縮的格式和各種設定的詳細資料。
-多級漸遠紋理,是Unity對遠處的物體使用的低解析度版本的紋理。如果我們的情境包含距離攝像機很遠的物體,我們可以通過使用多級漸遠紋理來緩解顯存頻寬的問題。Unity情境視圖中的The Mipmaps Draw Mode允許我們查看哪些物體受益於多級漸遠紋理,請參考this page of the Unity Manual 包含了使用多級漸遠紋理的詳細資料。
頂點處理
頂點處理是指GPU必須渲染網格中的每一個頂點的工作。頂點處理的消耗受兩件事情影響:必須渲染的頂點數量,以及在每個頂點上要進行的運算元量。
如果我們的遊戲是GPU限制,並且已經確認了不是填充率和顯存頻寬引起的問題,那麼就很可能是頂點處理引起的。如果是這種情況,那麼嘗試減少GPU頂點處理的數量很可能會獲得效能提升。
有一些方法可以減少頂點數量或者在每個頂點上執行的運算元量:
-首先,我們應該降低不必要的網格複雜的。如果我們使用的網格包含在遊戲中無法被看見的LOD,或者低效的網格在錯誤的建立時包含了太多的頂點,這些都會浪費GPU的工作量。最直接的降低頂點處理的消耗的方法,就是在3d建模軟體中建立模型時使用更少數量的頂點。
-我們可以嘗試使用法線貼圖技術,我們使用它來類比更高几何複雜度的網格。儘管使用這種技術有一些GPU負載,但是在多數情況下,它會獲得效能提升。請參考This page of the Unity Manual 介紹了使用法線貼圖技術去類比更複雜的網格。
-如果我們的遊戲沒有使用法線貼圖技術,在網格的匯入設定中,我們可以關閉頂點的切線。這會降低GPU處理頂點的資料量。
-LOD(Level of detail),這是當物體遠離攝像機時,降低物體網格的複雜度的技術。這可以有效降低GPU需要渲染的頂點數量,並且不影響視覺表現。具體使用細節請參考. The LOD Group page of the Unity Manual。
-頂點shader,是一段shader代碼,告訴GPU怎麼繪製每個頂點。如果我們的遊戲受限制於頂點處理的影響,那麼降低頂點shader的複雜度可能會有助於效能提升。
-如果我們的遊戲使用Unity內建shader,我們應該使用最簡單和最佳化的shader,為了達到我們想要的視覺效率。。例如,the mobile shaders是Unity針對移動平台高度最佳化的shader,我們應該實驗使用它們是否可以在不影響視覺效果的前提下改善效能。
--如果我們遊戲使用的是定製的shader,我們應該儘可能的最佳化它。最佳化shader是一個很複雜的主題,請參考this page of the Unity Manual 和 this page of the Unity Manual。
結論
我們已經學習了Unity中,是怎樣渲染的,當渲染時可能會發生什麼問題,以及怎樣在我們的遊戲中提高渲染的效能。使用這些知識以及效能分析工具Profiler,我們可以修複渲染相關的效能問題,並且構建我們的遊戲使他們具有流暢有效率的渲染流水線。
下面列出本文主題的相關資源
擴充閱讀
Unity Learn: A guide to optimizing Unity UI
Unity Knowledge Base: Why is my static batching breaking or otherwise not working as expected?
Fabian Giesen: A trip through the graphics pipeline
Simon Schreibt: Render hell
Gamasutra: How to choose between Forward or Deferred rendering paths in Unity
Gamasutra: Batching independently moving GameObjects into a single mesh to reduce draw calls
FlameBait Games: Optimizing SkinnedMeshRenderers for Unity 5
Pencil Square Games: Reducing draw calls (also named SetPass calls) in Unity 5
Unity效能最佳化(4)-官方教程Optimizing graphics rendering in Unity games翻譯