Unity渲染優化中文翻譯(二)——CPU的優化策略


   轉載請標明出處http://www.cnblogs.com/zblade/

  緊接上一篇文章,繼續渲染的優化問題,若有錯誤,請指出,讓我也學習進步,謝謝。

  如果游戲渲染問題來自CPU

  概括的來說,CPU在一幀的渲染中的工作可以分為三個部分:

  1)決定誰需要被渲染

  2)為GPU准備渲染指令

  3) 發送渲染指令給GPU

  在每個部分中又有許多單獨的任務,這些任務主要通過多個進程來執行。多進程確保渲染任務的並發執行,單個進程執行單個渲染任務,從而大大提高渲染性能。如果渲染任務被分配到多個進程進行,這就是多進程渲染。

  在渲染中主要有三種進程:主進程,渲染進程和工作進程。主進程主要負責CPU中的大部分工作,也包括渲染任務。渲染進程特定用來給GPU發送渲染指令。對於工作進程來說,每個工作進程執行單獨的任務,例如剔除或者網格的蒙皮。每個任務由哪個進程執行取決於游戲設置和運行時的硬件條件。例如,如果游戲運行硬件中CPU處理能力越強,就會有越多的工作進程產生。基於這個原因,我們的游戲在不同的硬件上會有不同的表現,我們可以基於目標硬件進行特定的游戲運行問題分析。

  由於多進程渲染非常復雜和硬件條件的原因,在進行渲染性能改進前需要定位CPU中的什么任務造成渲染問題。如果游戲運行較慢是由於某個進程中的剔除操作耗時較長,那么改進指令發送所在的進程對於性能的改進於事無補。

注意:並不是所有的平台都支持多進程渲染。在寫這篇文章的時候,WebGL並不支持這個特性。在不支持多進程渲染的平台上,所有的渲染任務都在同一個進程執行。在這樣的運行平台上,任何進程的優化操作都會提升渲染性能。如果渲染性能問題來自於CPU,那么我們需要詳細閱讀下面的部分並選擇合適的渲染優化方法改進游戲的渲染性能。

  1、Graphics jobs

  Player Settings中的graphics jobs決定Unity是利用主進程,渲染進程還是工作進程進行渲染任務。在可以設置的平台上,graphics jobs可以帶來較為可觀的性能改進。

  2、查找造成渲染問題的原因

  可以通過profiler window來定位造成渲染問題的具體任務,讓我們分析一下常見的性能問題及其解決辦法:

  1)給GPU發送渲染指令

  常見的CPU問題來自於指令發送時間過長,在大多數平台上,該任務處於渲染進程中,某些平台(PS4)處於工作進程中。

  在指令發送中,最耗時的操作是SetPass Call操作。如果游戲限制因素來自於指令發送,則需要減少SetPass的發送數量來優化游戲渲染性能。

  通過unity中的profiler window我們可以查看有多少SetPass被調用和多少批處理被渲染調用,SetPass可以被發送的數量受到目標硬件的限制,高配置的PC相對於移動設備可以發送更多數量的SetPass。SetPass和其相關的批處理的數量主要受到多個因素的影響,通常分為:

  (1)大多數情況下減少批處理的數量或者讓更多的object共享同一渲染狀態可以減少SetPass的調用次數;

  (2)通常情況下減少SetPass的調用次數可以提高CPU的性能。

   即使減少批處理的數量並沒有減少SetPass的調用次數,對於性能也是有一定的提升的。因為相比於多個批處理,CPU對於單個批處理的執行效率更高,即使兩者之間包含的數據量一樣。概括地說,有三種基本方法可以減少批處理和SetPass的數量:

  (1)減少渲染的objects的數量可以有效地減少批處理和SetPass的數量;

  (2) 減少每個object的渲染次數通常可以減少SetPass的數量;

  (3) 將必須渲染的object的數據進行合並可以減少批處理的數量。

  對於不同的游戲有不同的優化方法,所以我們需要全局考慮來選擇最適合我們游戲的方法。

  2)減少渲染的object的數量

  減少渲染的object的數量通常是減少批處理和SetPass數量最直接的方式,常用的方法有:

  (1)減少場景中可見的object的數量是最直接的方式。如果我們在場景中需要渲染大量的角色,通過減少場景中的角色的數量可以測試效果,如果其效果依然很好並且渲染性能提升,那么這就是最直接的提升性能的方法;

  (2) 可以通過設置相機的Far Clip Plane屬性來減小相機的渲染距離。深度值超過該屬性值的objects就不會被相機渲染。如果我們期望遠距離的物體不在可見,我們可以試着使用霧化效果來隱藏遠處物體的消失,可以參考此處:Fog to hide the lack of distant objects

  (3) 我們可以使用相機的LayerCullDistance屬性來根據物體的深度值來隱藏物體,在物體的前面有許多裝飾細節的時候,可以通過更小的距離來隱藏這些細節。

  (4) 我們可以采用Occlusion Culling來剔除那些被隱藏的物體,例如場景中如果有大量的建築,通過剔除技術可以讓被遮擋的物體不被渲染。unity中cull並不是適用任何場景,cull會造成額外的cpu消耗和增加設置的難度,但在某些場景中也可以極大的提高渲染性能。這兒詳細的介紹了culling技術。除了cull之外,我們也可以手動設置物體的可見與否。例如,如果我們的場景中包含大量的前置或者后置的不可見物體,則我們可以將其隱藏,根據我們游戲中的具體實際情況來優化相比於依賴於Unity的動態優化更可靠。

  3)減少必須被渲染物體的渲染次數

  實時光照,陰影和反射等措施會給游戲帶來更加逼真的效果,但是CPU的消耗也是極大。這些措施都會讓一個object被多次渲染,這對於游戲的性能有極大的影響。

  這些光照特性對於游戲的影響取決於游戲的渲染路徑設置。光照渲染路徑決定場景中的物體的之間的渲染計算關系,不同渲染路徑之間的差別主要體現在它們如何處理實時光照,陰影以及反射。通常來說,如果游戲運行在高端設備上,並且采用了較多的實時光照,陰影以及反射,延遲渲染路徑是一個較好的選擇。如果游戲運行在低端手機上而且不采用較多的光照特性,前向渲染路徑是更為合適的選擇。如果游戲希望采用實時光照,陰影和反射等特性,最好對於具體的游戲項目進行特定的設置。對於不同渲染路徑的設置,這兒有更多詳盡的闡述。對於unity中的光照和渲染,這兒有一些入門知識介紹。

  無論選擇何種渲染路徑,實時光照,陰影和反射對於游戲的運行性能都有較大的影響,所以對於他們的優化非常重要。

  (1) unity中的動態光照渲染十分復雜,深入的討論不在本文的范圍內。但是這兒有一些基本入門知識,這兒有一些對於游戲中的燈光的優化。

  (2)動態的光照渲染對於游戲性能的消耗較大,如果場景中包含較多的靜態的物體,我們可以采用baking(烘培)技術提前將光照的計算烘培到場景中,這樣就不需要進行實時的光照計算,這兒有對烘培技術的介紹,,這兒有對於烘培中光照的詳盡介紹。

  (3) 如果我們希望在游戲中添加陰影,那么可以對其進行較大的性能提升。這兒對於如何在Unity中設置陰影的屬性及其影響有較為詳細的介紹。比如我們可以設置shadow distance(陰影距離)來確保只有較近的物體才會被納入光照計算。

  (4) 反射屬性可以產生真實的光照反射效果,但是這對於批處理來說耗費較大。最好將反射的使用減小到最低,如果使用的時候盡可能優化。這兒介紹了如何優化游戲中反射的使用。

  4)將objects合並進更少的批處理中

  在條件滿足的情況下,一個批處理操作可以包含很多的objects。在以下情況下,可以將不同物體合並進一個批處理操作中:

  (1)共享相同的材質;

  (2) 材質的設置相同(比如:貼圖,shader,shader參數等)

  將滿足條件的物體進行批處理可以提高游戲的性能,然而與此同時我們必須仔細地分析來確保批處理的消耗不會超過優化帶來的提升(不然得不償失)。

  對於合適的物體的批處理,有一些技術可以采用:

  (1) 靜態批處理  在unity中可以將不移動的物體合並在一個靜態批處理中,例如可以將場景中不移動的石頭都合並進一個批處理中來提升游戲性能。這兒對於如何在游戲中設置靜態批處理有一些介紹。

  (2) 動態批處理 在unity中可以應用動態批處理來對游戲中的無論移動與否的物體進行處理,應用動態批處理對於物體有一些限制,這兒對其限制有詳細的列表解釋。動態批處理對於CPU的處理時間有更大的消耗,在進行動態批處理的測試的時候最好注意這一點而小心使用動態批處理技術。

  (3) UI中的批處理由於受到層級的設置影響而顯得十分復雜,UI批處理視頻介紹了一些優化方法,這兒介紹了一些方法來確保UI中的批處理優化達到我們期望的效果。

  (4) GPU實例化 unity中可以采用GPU實例化來將物體有效的批處理,盡管有一些限制而且不是所有的硬件都支持,但是如果我們的游戲中同屏物體較多,可以嘗試采用此方法。這兒對於在unity中如何使用GPU實例化方法有詳盡的解釋,而且對各個平台都有較為詳盡的闡述。

  (5) 圖集 圖集主要是將多個貼圖打包進一個,在2D游戲和UI中應用較多,3D游戲中也可以采用此方法。在設計制作游戲中的美術時可以采用圖集方法來講那些共享相同貼圖的物體合並在一起進行批處理操作。unity中提供了圖集的制作工具Sprite Packer

  (6)可以在unity的編輯器模式下或者實時運行的時候將共享相同材質和貼圖的網格合並在一起。在進行合並操作的時候,我們需要明確陰影,光照,和剔除等操作任然在每個物體上運行。這會加大游戲性能的消耗,從而中和通過剔除操作帶來的性能優化。如果我們研究該方法,我們需要檢查Mesh.CombineMeshes方法。

  (7) 在操作腳本中的Renderer.material方法時需要及其小心,這會復制生成當前的貼圖從而返回一個指向該新的拷貝的指針。由於當前渲染不再指向同一個材質的實例化對象,如果當前渲染處於批處理中,這會打斷批處理操作。如果我們想要獲取批處理中objects的材質,最好采用Renderer.sharedMaterial.

  5)剔除,排序和批處理

  將需要渲染的objects的數據進行剔除和批處理排序操作,然后生成對應的GPU指令都會對CPU的性能瓶頸有影響。這些任務要么在主進程上運行,要么在單獨的工作進程上運行,這取決於游戲的設置和目標硬件。

  (1) 剔除操作本身並不耗費性能,但是減少不必要的剔除操作可以提升游戲的性能。對於場景中的各個激活的物體都有一個單獨的相機,即使那些不被渲染的物體。為了提升性能,可以關閉那些當前不被使用的相機或者將其渲染不使能。

  (2) 批處理操作可以提升發送給GPU的指令的速度,但是有時這會造成不必要的消耗。如果批處理操作造成了CPU的瓶頸,最好減少人工的或者自動的批處理操作數量。

  6)網格皮膚

  SkinnedMeshRenders在網格的骨骼動畫中常常被使用,特別是在角色的動畫中。網格皮膚的渲染任務常常在主進程或者單獨的工作進程中進行,這取決於游戲的設置和目標硬件。

  網格皮膚的渲染極其消耗性能,如果在profiler window中查看到網格皮膚的渲染造成CPU瓶頸,下面有一些方法可以用來提升性能:

  (1) 對於游戲中的每個物體我們需要考慮是否需要采用SkinnedMeshRender組件,也許游戲中的模型並不是需要該組件,這時候可以用MeshRender組件來代替從而提升性能。如果在將模型導入unity的時候如果不選擇導入動畫,模型就會用MeshRender組件來代替SkinnedMeshRender組件。

  (2) 如果我們的模型只是偶爾會有一些動畫(比如開場或者距離較遠時),則可以用MeshRender組件來替代SkinnedMeshRender組件。SkinnedMeshRender組件有一個BakeMesh函數用來創造合適的網格,這樣可以在不改變視覺效果的情況下交換不同的網格或者渲染。

  (3)對於采用網格皮膚的角色動畫,這兒有一些優化方法來提升性。除此之外,每個頂點都會加大網格皮膚的渲染,所以減少模型的頂點數量會極大的提升游戲的性能,模型頂點數量越少越好。

  (4)在一些平台傷,網格皮膚的渲染可以放在GPU傷進行,如果GPU中包含較大的容量,可以測試將其移植到GPU中進行渲染,可以在Player Settings中進行相關的設置。

       7)主進程中與渲染無關的操作

  CPU中的主進程會同時進行多個任務,這意味着減少與渲染無關的任務的時間可能會提高CPU的性能。比如CPU主進程同時進行高消耗的渲染工作和腳本操作,如果已經將渲染操作盡可能的優化了,那么我們可以嘗試優化腳本的性能。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM