(四)unity中的渲染優化技術——————(遊戲物體、UI、模型的優化,以及移動平臺的一些優化)

一、渲染優化的元素

和渲染優化相關的東西很多,大致可分爲網格、着色器材質、光照和陰影。相關的優化技術有相機視椎體剔除、遮擋剔除、基於層的分類剔除與合併繪製調用。LOD降級分爲着色器的LOD降級和LodGroup降級。在所有這些方面地形是比較特殊的,假設使用的是unity的地形,可以將這些與性能優化有關的元素整理成下圖:

 1.1 小型物體的優化

小型物體不能像一堵牆或桌子那樣提供遮蔽,按照功能,小型物體可以分爲非功能性的和功能性的。

對於以下非功能性的小型物體的解釋:

1.靜態物體的圖集。如果圖集是靜態的,處在同一個角落(屋子或營地)的小型物體可以合併貼圖到一個圖集中,並使用同一種材質。對於unity而言,如果你使用了零散貼圖也沒關係,可以設置打包的標籤,讓Sprite Packer幫你打包,這樣得到的也是合併的圖集。

一個角落有一個圖集,這種圖集應該是靜態小物體貼圖的合併。有些小型物體可能會出現在地圖上各個角落,如果出現頻率不是特別高,只在五六個地方反覆出現,可以考慮地圖上的冗餘,即保持一個角落有一個圖集。

如果這個小型物體的出現頻率特別高,不合併到圖集上,或者合併到一個單獨的高頻率物體的圖集中也行,如子彈。

2.動態物體的圖集也應該專門放到動態物體準備的圖集中,使用同一種材質來製作。

動態小型物體應該嚴格限制頂點數量,運行時的合批有一個的CPU計算、空間轉換,而且unity合批的上限是900個與頂點相關的通道(目前)。

小型物體應該放置到一個單獨的圖層上(根據功能性也可以分別放到兩個圖層上)。小型物體沒有遮蔽功能,而且是繪製調用數量的一個主要來源,應該對其使用基於層的、較小的剔除距離。

小型物體的遮擋剔除的優化重點,但也有問題,如烘焙精細度和精確度,對於中型物體,比如汽車,在視覺上和功能上都比較重要,可以考慮LOD Group這種不同細節的模型,保證視覺上的存在和遠距離渲染的簡化。

對於小型物體,不建議使用LOD Group。對於處於一個閉塞空間(如房子內使用的小物體),可以設定一個更小的剔除距離,比如設爲房子大小的3倍距。相對於空曠的空間,從外面觀察房間內一般都比較昏暗,因此看不清楚/無法發現房間內的小型物體是正常的,邏輯上講得通。

如果閉塞空間內的物體可以被使用,比如被玩家帶到了室外,那麼就應該改變其在層上的剔除距離。閉塞空間內的物體本應該使用遮擋剔除來解決,但是使用遮擋剔除有代價和精度問題。如果可以使用遮擋剔除來解決,我們先在層的這個概念上做好優化。

1.2中型物體的優化

對於建築這種中型物體或者對於岩石這種網格細節密度比較大的物體,可以根據物體在屏幕上的大小使用LODGroup,動態切換/使用不同細節的網格,從而調節渲染性能的表現效果,這需要在項目規劃階段規劃好時間。

但是LOD的這種功能是以冗餘(內存爲代價的),還會給美術添加額外的建模負擔。不過對於建築這種大方塊物體,在較遠的距離上,可以使用一個比立方體簡單的表達方式。此外這種方式影響的只是“物體”的渲染,與物理碰撞沒有關係,也就是說不會影響看不見的運算邏輯。

1.3大型物體的優化

unity的遮擋剔除和相機的剔除功能是並行的,可以在基於View Frustum剔除的基礎上進一步剔除那些不會出現在視野內的小物體。遮擋剔除主要靠烘焙靜態物體之間的遮擋,將它們存儲在一個樹狀結構中,然後在運行時查詢,從而剔除那些被遮擋的、不會出現在視野內的物體。

但是遮擋剔除也有問題,首先遮擋剔除會線下烘焙、生成數據,這個線下處理中的時間不是問題,問題是它所產生的數據。根據場景的大小以及烘焙的細分粒度,會產生10KB到10MB的數據,如果遮擋剔除粒度太粗,可能會剔除失敗,比如被牆擋住的方塊並未被剔除掉。

如果簡單地增加遮擋剔除的粒度,就會使遮擋剔除的數據膨脹,並且增加運行時CPU查詢表結構的負擔,因此這也是一個效能和開銷之間平衡的問題。

另外從遮擋剔除的角度來說,物體被分割成小塊,有利於增加遮擋剔除的效率。但把散亂、不同角度的小型物體連成一個大型物體,不利於遮擋剔除。

1.4模型的優化

下面介紹建模的相關概念。

在建模軟件(比如Max 和Maya)中幾何體的同一位置的點就是一個點。但是在unity中,準確地說,在GPU上,點的意義對應於三角面和相鄰的三角面。如果定義它們的頂點的法線不一致,就是不能共享的一個頂點。軟邊就是指相鄰三角面共享同一個頂點,它的法線以及所有和頂點相關的性質(如切線、頂點色)和UV都共享。而硬邊則意味着相鄰的面具有不同的頂點,對同一個位置頂點的切割意味着更多的數據、內存,以及提交到GPU的數據。

角色建模大多涉及的是軟邊,像劍這種切割武器則涉及的是硬邊。

當然不能爲了更少的數據要求降低模型精度,這只是一個值得注意的東西,如果有些東西的軟邊可以表達,就不必使用硬邊了,比如杯子,你可以說它是一個帶棱的杯子,也可以說它是一個帶弧的杯子(這種情況下就選擇軟邊)。模型的軟邊和硬邊如圖所示:

對於模型來說,爲了計算光照,法線一般是必需有的,但是切線不是必需有的。同時具備切線和法線一般是爲了計算切線空間內的數據,比如法線凹凸。對於渲染,這並不是一個必選項。而在unity中切線是一個Vector4,如果是一個由1024個頂點的模型,就可以省下16KB的內存。

對於一個模型來說,一般頂點位置、法線以及一套uv是必需有的,有些情況下會使用到頂點色來配合shader做表現。對於靜態物體,如果使用unity的光照貼圖,還需要一套uv,可以勾選fbx的Generate Lightmap UVs(需要烘焙的fbx都要勾這個):

這個uv用來在一個大的光照貼圖中定位貼圖,如果不勾然後烘焙,會出現uv亂序,它會默認指定一個沒有拆分uv的圖來烘焙,導致烘焙的不對。如果在建模軟件中烘焙,這套uv不是必需有的,使用與diffuse貼圖相同的uv就可以(如果diffuse貼圖沒有平鋪現象)。unity勾選Generate Lightmap UVs生成的uv在烘焙的lightmap中會佔用大面積的圖塊,而且分佈是亂序的,我們選擇一個prefab可以看到:

如果在建模軟件烘焙,我們可以使uv順序鋪滿一個圖塊,節省圖塊大小,例如:

另外如果在拆分uv時同一個位置的點具有不同的UV,也會導致產生新的頂點。

UV是一個2D向量,如果假設模型有1024個頂點,一套UV是8KB。

對於凸出的裝飾物,應該避免對一個整面進行切割擠出,這樣做不會減少必要的點,反而會增加面。裝飾物應該儘量使用軟邊,烤箱是沒辦法使用硬邊的。

刪除或者合併在視覺上過小的面。

避免過大的面,插入必要的線進行分割,這個分割操作考慮光照和麪剔除的因素。

如果使用Max,儘量使用釐米作爲單位,這樣在Max裏100個單位是一米,如果使用默認的英制單位,在導入時的單位轉換就不會這麼直觀了。

可以考慮爲角色刷一套頂點色,用於控制/加權實時照明和環境光。 

1.5 地形的優化

地形的數據和運行時細節管理都是通過Unity的Terrain Engine來完成的,unity提供了一套基於距離的內置LOD控制系統,可以提供各種細節調整。

地形的LOD控制有3個方面:

1.地形自身的網格、網格細分密度;

2.地形的渲染複雜度、陰影、材質和光照;

3.地形上的修飾物(樹木、草)。

對於這三個方面Unity均提供了相關優化或性能效果的平衡參數。

下面這些參數在運行時可修改。

地形網格的密度參數如下表所示:

上面這些參數的功能在表現效果上有重複,或者說內部實現途徑不一樣。

地形網格的渲染複雜度如下表:

 地形上的修飾物(樹木、花草)參數如下表:

這些參數有些是開關式的,如cast shadows、drawHeightmap以及 drawTreesAndFoliage,不過大多數參數是與距離相關的,這給了我們很多連續性調節性能和效果的空間,尤其是我們將地形分開的時候。下圖是距離和LOD的關係圖:

 根據當前角色的位置,找出當前那塊地形,然後依次找出臨近的地形塊,以及遠處地形塊(當前地形可能多至4塊,或恰好在4塊地形的交界處)。

對地形分塊,並且根據距離角色的位置分類之後,就可以對不同的地形塊應用不同的LOD參數,在空間距離上調整細節。

地形上有很多細節,但是unity的地形爲我們提供了現成的接口來做自適應的網格細分降級、細節的LOD降級、視距上的渲染剔除。這些和我們自己創建的物體不同(比如劍、槍和食物等),我們自己創建物體需要在設計時就考慮合併貼圖到圖集,放置到合適的圖層,設置相機在這個圖層的剔除距離等。

對於地形優化,可以考慮分塊,這個可以用第三方插件,如T4M,以及地形的動態加載/卸載(當地圖面積很大時)。如果地圖面積不大,只有幾個區塊,並且不論什麼情況下都會出現在視野內,就沒有必要做動態加載/卸載這塊,只需要考慮LOD調整就行了。

1.6UI的優化

UI的優化關注的是圖集的合理切分、Overdraw和網格的刷新,也就是動態元素和靜態元素的分離。

1.動態的UI元素

尤其是那些高頻、幾乎每幀都會更新Position、Rotation或者Scale的UI元素,應該單獨放在一個畫布下面,避免更新靜態的UI元素,使Canvas.BuildBatch的CPU開銷最小化。

如果動態UI元素無法避免,應該將其放置到RectTransform層級的最底層,否則會級聯觸發OnTransformChanged事件。

2.UI元素產生的繪製調用

每一個獨立的畫布和圖集都會打斷對UI元素的批處理,產生額外的繪製調用,在UI上使用自定義材質,也會打斷對UI元素的批處理,增加繪製調用次數。

對於UI的繪製調用開銷的關注應該是相對的、有條件的。比如,在繪製調用比較緊張的地方(遊戲的戰鬥場景),應該注意UI的性能以及繪製調用。但是在一個獨立的、只以UI元素爲主的地方,比如遊戲選擇界面或加載時等地方,UI的性能和繪製調用就不會那麼敏感,因爲這個時候不會有擠佔CPU或GPU時間的問題。

3.UI的圖集劃分

應該儘量確保一個情境下的UI元素使用一個圖集,即使偶爾有幾個精靈在多個UI情境下出現,如果這些精靈並不大,應該使用冗餘的方式保證圖集的獨立,而不是企圖在一個UI情境下使用多個圖集,僅僅是爲了複用幾個小小的精靈,這不利於內存和資源的管理。

最後在創建UI元素的時候,會使用內置的一套默認圖集,這個圖集應該在所有UI元素上棄用。

4.避免UI元素上的重繪

UI元素是按照透明物體的模式,從後往前渲染的,而不論具體UI元素是否透明,所以當UI面板展開,UI元素一層疊一層的時候,重繪在很多情況下是不可避免的,但對於靜態的元素重繪則可以優化。

因爲透明物體的正確渲染需要嚴格按照從後往前的順序,所以在兩個UI元素之間插入一個異類(使用了不同的圖集、材質等方式)會打斷UI元素的合批,產生額外的繪製調用。

5.UI上的文字

一個字母使用一個Quad渲染,所以界面上的文字要言簡意賅。UGUI對字體很敏感,如果兩個文字大小不同,或者樣式不一致,字體文件中就會包含一個字母的多種形式。所以不要使用Test組件的BestFit特性,這會在字體文件中存放大小各異的字形

Font文件可以根據需要分成兩類,即靜態字體文件和動態字體文件。第一類主要是有UI標題和Label等,而第二類主要是姓名輸入、即時消息。第一類可以做到極大的優化,只包含那些已經使用的字符。而第二類對於unity來說則是一個動態維護字形(glyphs)的貼圖。

6.小心區塊格式

這個格式會根據源精靈的大小產生網格,如果平鋪精靈像素很小,平鋪的區塊會產生很多頂點和三角面。

7.UI和完美像素

在ScreenOverlay模式下會強制UI元素的邊界和像素對齊,這會產生額外的CPU計算,應該慎用。

8.UI上的物理屬性

對於物理屬性,也需要避免遮蓋。對於靜態的UI元素,如果存在遮蓋現象,應該只保留一個其中的一個Raycast Target屬性。UI上的物理屬性。

9.其他

如果有大面積漸變色,首先,一般的壓縮會破壞這種線性漸變色,其次會產生很大的貼圖。對於這種需求,可以考慮繼承UI中的Graphic類,通過自定義頂點色和GPU的線性插值產生漸變色,當然這種方式不太直觀和方便。

1.7物理引擎

實現物理碰撞時要做好層的劃分,設置好FixedUpdate的頻率、平衡物理引擎的性能。另外,如果遊戲要進行多人聯機,一定要注意物理引擎的使用。

考慮不同平臺浮點運算單元的差異,如在計算浮點數時可能會產生極其微小的差異。如果使用狀態同步產生的差異還可以接受(受制於需要同步的狀態量,如果玩家需要控制同屏的許多個角色或對象,狀態同步的數據量是不可接受的)。如果使用幀同步(受制於同步的指令量,同場的幾百個玩家一起玩,則玩家的網格環境差異造成的體驗也不太好),這種差異很可能被不斷放大,造成執行效果在不同設備上完全不同。對於這種情況,需要禁止使用unity的物理引擎部分或禁用全部功能,而自己實現一套基於整數的物理引擎(部分數學計算通過查表和放大幾百倍、幾千倍實現),這樣不同平臺下的計算結果就不會存在差異,一般而言,整數計算速度也比浮點數高。

1.8慎用後期效果

不推薦使用後期效果,這是一個逐像素的全屏操作。如果要,使用小一點的渲染紋理,且儘可能減少片元着色器的計算量。對於不同性能的目標平臺,建議在系統設置當中暴露接口後綴使用一定的條件判斷,允許玩家手動取消或者遊戲自動取消某些後期效果。

如果可能,則把最終效果所需要的計算儘量集中到一個通道中完成。

1.9慎用透明效果

不建議使用透明效果,如果可以,儘量多添加幾個點來表達形狀,透明意味着unity要排序,在GPU中逐像素地渲染,所以要儘量避免。

如果必須使用,一定要儘可能將多個透明圖層進行合併,因爲透明必將帶來重繪問題,對於透明物體儘可能剔除正面/背面,減少渲染的像素數量。

儘可能減少對深度測試、剔除極不友好的操作,比如剪切、Alpha測試等操作,這些操作會直接使用Early-Z、隱藏面消除等技術作廢。同時對現代的並行優化GPU而言(進行上一個問題的光柵化過程的同時還可以利用剩餘的處理能力進行下一個物體的幾何過程),這些操作也會造成這些優化失效。如果可能,使用混合來實現透明效果,但是即使使用混合,也要儘量避免透明物體的疊加,因爲每一個透明物體的渲染都會迫使GPU對於每個像素執行一次片元着色器。

1.10其他

粒子關注的重點則是重繪,美術人員對效果的追求是毫無節制的,但是對性能則是不敏感的,所以對美術人員提交的粒子特效需要重點關注。

關於陰影,陰影的caster一般是不輸出顏色的,對於不同的材質的角色/物體,其產生陰影的材質是可以相同的。也就是說可以合批。

關於貼圖,Unity爲我們提供的壓縮選項都是硬件壓縮,也就是貼圖資源,它們在內存中都處於壓縮狀態,但需要注意的是一旦平臺不支持當前貼圖的壓縮格式,unity就會把貼圖解壓縮到內存中,通常使用unity默認的壓縮格式解決大多數問題。

1.11移動平臺的特點

相對於PC來說,移動平臺不論是內存還是運算速度,都相差了至少一個數量級,而且移動平臺一般使用的是電池,因此其最大負荷是有限的,這就使我們不得不考慮如何最大化地優化着色器中的代碼,使其提高運行效率。

1.11.1 一些指令的運算速度

首先對於現代的PC顯卡,已經很智能了,能夠識別出一個y*y*y*y爲pow(y,4),並對應地做優化,比如優化爲兩個y*=y,y*=y,這樣就可以把運算從4次減少爲3次,但是移動平臺目前還無法做到這一點,因此你可能不得不使用pow指令,或者在着色器的代碼中手動進行數學運算上的優化。

其次,現在的PC顯卡上,數學運算指令的非常快的,基本上不用考慮幾條數學運算指令的代價,但是移動平臺的情況不是這樣。pow、sin、cos這些數學函數在移動平臺上的運算速度很慢,因此要儘量避免使用它們,把這些計算移到vertex函數中,或者使用一張小的貼圖,通過查表來代替一些複雜的數學運算如果某些運算確定只需要執行一次,那麼把他移動到CPU端計算並傳給GPU,性能會得到提升。即使把這種計算交給頂點着色器而不是片元着色器,也比CPU計算一次節約計算力,除此之外還要注意,noise()這個在標準Cg函數庫中的函數,在絕大多數主流平臺上未實現,在大多數使用這個函數的情形下,使用噪聲貼圖替代是個更好的方法。

對於tex2D/texCUBE指令,在移動平臺上,一個讀取貼圖的指令和一個普通的數學運算指令消耗大概相差至少一個數量級;在PC平臺上,這個差別更大,大概在兩個數量級以上,這是因爲在PC平臺上,數學運算指令比tex2D的速度快了很多

1.11.2 幾何複雜度

頂點數量可以成爲一個影響渲染效率的重要閾值,在IOS上,一個參考值是每一幀渲染的物體,當前視口內的頂點總數不要超過10的6次方個,因爲這是一個IOS底層驅動默認的頂點數據緩衝區的大小,超過這個數字,很可能導致驅動底層做一些開銷巨大的分割操作。通常而言,將這個數值保持在10的四次方~2*10的四次方是比較理想的值,因爲如果考慮做跨平臺的遊戲開發,這個數字對於絕大多數的Android 設備也能接受,實際生產中,建議採用LOD Group和遮擋剔除等技術來減少頂點數

1.11.3 貼圖的問題

對於貼圖,其大小應該儘量是2的冪次方,因爲所有的計算和存儲最終都要以2的冪次方爲單位。儘管平臺可能支持非2次方冪的貼圖,而且可能支持得很好,但是它存儲和查找的效率總不會超過最接近的2的冪次方貼圖。

在各種移動平臺上,能支持的最大貼圖是多少?實際開發中應該使用多大貼圖?平臺能支持的最大貼圖和硬件密切相關,即使同爲Android平臺,因爲硬件不同,所支持的最大貼圖也是不一樣的。PowerVR SGX540所支持的最大貼圖爲2048X2048,Tegra 2位2048X2048,Adreno 200爲4096X4096,Mali 400MP爲4096X4096。

那麼GPU所支持的最大貼圖是不是最佳的貼圖尺寸?對於這個問題,爲了應用在平臺間移植的可能性,建議以1024爲標杆,最大不要超過2048。在IOS遊戲《無盡之劍》內,場景內的角色模型大概使用了3000個頂點,角色的貼圖卻高達2048個,但是對於移動應用來說,3000個頂點的模型已經是高模了,但是這個遊戲裏任何時刻最多隻會出現2個角色,所以纔會把角色頂點數設置的這麼多。

對於貼圖的mipmaps選項,如果遊戲是3D,記得一定要打開mipmap選項,如果關閉了該選項,在iPad上只會導致大概3ms的性能損失,但是在三星的Tegra上可能是宕機。

當把所有的貼圖導入unity中時,都會把它們處理爲unity所支持的格式,這種處理包括分類、紋理壓縮、mipmap的製作等。使得任何貼圖文件的原始尺寸和類型與最終發佈時的尺寸和類型完全無關,這意味着可以使用自己的格式和尺寸大小來保存貼圖文件,而在發佈應用時使用一個平臺相關的大小和類型。

ETC、DXT和PVRTC都是硬件壓縮,如果硬件不支持,而你又使用了,那麼將在貼圖加載時由CPU來解壓縮。建議使用unity默認的壓縮格式,它會自動針對平臺進行適配並壓縮紋理,可以在圖像的質量、硬件處理效率和圖像大小之間得到良好的平衡。

ETC是在Android平臺上硬件普遍支持的一種壓縮格式,推薦在Android上使用ETC的壓縮格式,但是ETC不支持Alpha通道。但是ETC不支持Alpha通道。因此當貼圖含有Alpha通道時,Unity默認在貼圖大小、質量以及渲染速度之間的平衡格式是RGBA-16bit。不過對於硬件Tegra,最好使用DXT5的貼圖壓縮方式。

如果你的目標平臺是IOS或者PowerVR,應選擇PVRTC的貼圖壓縮格式。

1.11.4 數據類型的使用方式

fixed或者說lowp的精度是8位,在OpenGL ES2.0擁有的最小值域爲[-2,2],適合作爲顏色和其他單位化的方向矢量,half或者說mediump的精度爲16位,比較適合表示三維空間的座標和,用於進行數學運算的標量。儘管大多數現在PC的GPU裏,fixed直接當做half處理,但移動GPU基於能耗 考慮仍然保留並處理fixed精度。所以一旦確定了特定變量的變化範圍在某個精度允許的範圍內,儘可能使用這種精度處理。

不論是PC還是移動的GPU,其雙精度(double)的處理效率相比單精度直接降低了一個數量級,所以儘可能不要使用雙精度來處理。

在移動平臺上,關於這些數據類型的運算效率,可以這樣考慮:half/mediump是float/highp的兩倍,fixed/lowp又是half/mediump的兩倍。除此之外儘量避免在這三個數據類型之間進行轉換,也避免在fixed/lowp上做調配操作。這些操作都會引擎性能和精度的損失。最後,Adreno是個例外,Adreno的硬件都這類精度並不敏感。

1.11.5 變量的使用

大多數GPU上會盡量減少從vertex函數傳遞到fragment函數的參數數量,把變量包裝起來,比如把兩個fixed2打包到一個fixed4中,但是在PowerVR上,也就是IOS的GPU上不上這樣,PowerVR對變量數量不敏感,其次如果變量是用來讀取貼圖的UV變量,儘量使用獨立的UV變量,不要使用一個四元數來包裝兩個二元數。在進入fragment函數前,如果可能,確定uv,這樣PowerVR就能提前讀取貼圖的值,從而避免在fragment函數中讀取貼圖。而在移動平臺上,tex2D是一個消耗性能的操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章