Unity 性能優化 Draw Call

Unity(或者說基本所有圖形引擎)生成一幀畫面的處理過程大致可以這樣簡化描述:引擎首先經過簡單的可見性測試,確定攝像機可以看到的物體,然後把這些物體的頂點(包括本地位置、法線、UV等),索引(頂點如何組成三角形),變換(就是物體的位置、旋轉、縮放、以及攝像機位置等),相關光源,紋理,渲染方式(由材質/Shader決定)等數據準備好,然後通知圖形API——或者就簡單地看作是通知GPU——開始繪製,GPU基於這些數據,經過一系列運算,在屏幕上畫出成千上萬的三角形,最終構成一幅圖像。

在Unity中,每次引擎準備數據並通知GPU的過程稱爲一次Draw Call。這一過程是逐個物體進行的,對於每個物體,不只GPU的渲染,引擎重新設置材質/Shader也是一項非常耗時的操作。因此每幀的Draw Call次數是一項非常重要的性能指標,對於iOS來說應儘量控制在20次以內,這個值可以在編輯器的Statistic窗口看到。

Unity內置了Draw Call Batching技術,從名字就可以看出,它的主要目標就是在一次Draw Call中批量處理多個物體。只要物體的變換和材質相同,GPU就可以按完全相同的方式進行處理,即可以把它們放在一個Draw Call中。Draw Call Batching技術的核心就是在可見性測試之後,檢查所有要繪製的物體的材質,把相同材質的分爲一組(一個Batch),然後把它們組合成一個物體(統一變換),這樣就可以在一個Draw Call中處理多個物體了(實際上是組合後的一個物體)。

但Draw Call Batching存在一個缺陷,就是它需要把一個Batch中的所有物體組合到一起,相當於創建了一個與這些物體加起來一樣大的物體,與此同時就需要分配相應大小的內存。這不僅會消耗更多內存,還需要消耗CPU時間。特別是對於移動的物體,每一幀都得重新進行組合,這就需要進行一些權衡,否則得不償失。但對於靜止不動的物體來說,只需要進行一次組合,之後就可以一直使用,效率要高得多。

Unity提供了Dynamic Batching和Static Batching兩種方式。Dynamic Batching是完全自動進行的,不需要也無法進行任何干預,對於頂點數在300以內的可移動物體,只要使用相同的材質,就會組成Batch。Static Batching則需要把靜止的物體標記爲Static,然後無論大小,都會組成Batch。如前文所說,Static Batching顯然比Dynamic Batching要高效得多,於是,Static Batching功能是收費的……

要有效利用Draw Call Batching,首先是儘量減少場景中使用的材質數量,即儘量共享材質,對於僅紋理不同的材質可以把紋理組合到一張更大的紋理中(稱爲Texture Atlasing)。然後是把不會移動的物體標記爲Static。此外還可以通過CombineChildren腳本(Standard Assets/Scripts/Unity Scripts/CombineChildren)手動把物體組合在一起,但這個腳本會影響可見性測試,因爲組合在一起的物體始終會被看作一個物體,從而會增加GPU要處理的幾何體數量,因此要小心使用。

對於複雜的靜態場景,還可以考慮自行設計遮擋剔除算法,減少可見的物體數量同時也可以減少Draw Call。

總之,理解Draw Call和Draw Call Batching原理,根據場景特點設計相應的方案來儘量減少Draw Call次數纔是王道,其它方面亦然。







Draw Call Batching (繪製調用批處理)

 

To draw an object on the screen, the engine has to issue a draw call to the graphics API (OpenGL ES in the case of iOS). Every single draw call requires a significant amount of work on the part of the graphics API, causing significant performance overhead on the CPU side.

在屏幕上渲染物體,引擎需要發出一個繪製調用來訪問圖形API(iOS系統中爲OpenGL ES)。每個繪製調用需要進行大量的工作來訪問圖形API,從而導致了CPU方面顯著的性能開銷。

 

Unity combines a number of objects at runtime and draws them together with a single draw call. This operation is called "batching". The more objects Unity can batch together, the better rendering performance you will get.

Unity在運行時可以將一些物體進行合併,從而用一個繪製調用來渲染他們。這一操作,我們稱之爲“批處理”。一般來說,Unity批處理的物體越多,你就會得到越好的渲染性能。

 

Built-in batching support in Unity has significant benefit over simply combining geometry in the modeling tool (or using theCombineChildren script from the Standard Assets package). Batching in Unity happensafter visibility determination step. The engine does culling on each object individually, and the amount of rendered geometry is going to be the same as without batching. Combining geometry in the modeling tool, on the other hand, prevents effecient culling and results in much higher amount of geometry being rendered.

Unity中內建的批處理機制所達到的效果要明顯強於使用幾何建模工具(或使用Standard Assets包中的CombineChildren腳本)的批處理效果。這是因爲,Unity引擎的批處理操作是在物體的可視裁剪操作之後進行的。Unity先對每個物體進行裁剪,然後再進行批處理,這樣可以使渲染的幾何總量在批處理前後保持不變。但是,使用幾何建模工具來拼合物體,會妨礙引擎對其進行有效的裁剪操作,從而導致引擎需要渲染更多的幾何面片。

 

Materials

材質

Only objects sharing the same material can be batched together. Therefore, if you want to achieve good batching, you need to share as many materials among different objects as possible.

只有擁有相同材質的物體纔可以進行批處理。因此,如果你想要得到良好的批處理效果,你需要在程序中儘可能地複用材質和物體。

 

If you have two identical materials which differ only in textures, you can combine those textures into a single big texture - a process often calledtexture atlasing. Once textures are in the same atlas, you can use single material instead.

如果你的兩個材質僅僅是紋理不同,那麼你可以通過 紋理拼合 操作來將這兩張紋理拼合成一張大的紋理。一旦紋理拼合在一起,你就可以使用這個單一材質來替代之前的兩個材質了。

 

If you need to access shared material properties from the scripts, then it is important to note that modifyingRenderer.material will create a copy of the material. Instead, you should useRenderer.sharedMaterial to keep material shared.

如果你需要通過腳本來訪問複用材質屬性,那麼值得注意的是改變Renderer.material將會造成一份材質的拷貝。因此,你應該使用Renderer.sharedMaterial來保證材質的共享狀態。

 

Dynamic Batching

動態批處理

Unity can automatically batch moving objects into the same draw call if they share the same material.

如果動態物體共用着相同的材質,那麼Unity會自動對這些物體進行批處理。

 

Dynamic batching is done automatically and does not require any additional effort on your side.

動態批處理操作是自動完成的,並不需要你進行額外的操作。

 

Tips:

提醒:

1、      Batching dynamic objects has certain overheadper vertex, so batching is applied only to meshes containing less than900 vertex attributes in total.

             批處理動態物體需要在每個頂點上進行一定的開銷,所以動態批處理僅支持小於900頂點的網格物體。

 

2、      If your shader is using Vertex Position, Normal and single UV, then you can batch up to 300 verts and if your shader is using Vertex Position, Normal, UV0, UV1 and

            Tangent, then only 180 verts.

            Please note: attribute count limit might be changed in future

            如果你的着色器使用頂點位置,法線和UV值三種屬性,那麼你只能批處理300頂點以下的物體;如果你的着色器需要使用頂點位置,法線,UV0UV1和切向量,那你只

            能批處理180頂點以下的物體。

            請注意:屬性數量的限制可能會在將來進行改變。

 

4、      Don't use scale. Objects with scale (1,1,1) and (2,2,2) won't batch.

            不要使用縮放尺度(scale)。分別擁有縮放尺度(1,1,1)(2,2,2)的兩個物體將不會進行批處理。

 

5、      Uniformly scaled objects won't be batched with non-uniformly scaled ones.

            統一縮放尺度的物體不會與非統一縮放尺度的物體進行批處理。

           Objects with scale (1,1,1) and (1,2,1) won't be batched. On the other hand (1,2,1) and (1,3,1) will be.

           使用縮放尺度(1,1,1) (1,2,1)的兩個物體將不會進行批處理,但是使用縮放尺度(1,2,1)(1,3,1)的兩個物體將可以進行批處理。

 

6、     Using different material instances will cause batching to fail.

           使用不同材質的實例化物體(instance)將會導致批處理失敗。

 

7、     Objects with lightmaps have additional (hidden) material parameter: offset/scale in lightmap, so lightmapped objects won't be batched (unless they point to same

           portions of lightmap)

           擁有lightmap的物體含有額外(隱藏)的材質屬性,比如:lightmap的偏移和縮放係數等。所以,擁有lightmap的物體將不會進行批處理(除非他們指向lightmap的同一

           部分)。

 

8、     Multi-pass shaders will break batching. E.g. Almost all unity shaders supports several lights in forward rendering, effectively doing additional pass for them

           多通道的shader會妨礙批處理操作。比如,幾乎unity中所有的着色器在前向渲染中都支持多個光源,併爲它們有效地開闢多個通道。

 

9、     Using instances of a prefab automatically are using the same mesh and material.

           預設體的實例會自動地使用相同的網格模型和材質。

 

Static Batching

靜態批處理

 

Static batching, on the other hand, allows the engine to reduce draw calls for geometry of any size (provided it does not move and shares the same material). Static batching is significantly more efficient than dynamic batching. You should choose static batching as it will require less CPU power.

相對而言,靜態批處理操作允許引擎對任意大小的幾何物體進行批處理操作來降低繪製調用(只要這些物體不移動,並且擁有相同的材質)。因此,靜態批處理比動態批處理更加有效,你應該儘量低使用它,因爲它需要更少的CPU開銷。

 

In order to take advantage of static batching, you need explicitly specify that certain objects are static and willnot move, rotate or scale in the game. To do so, you can mark objects as static using the Static checkbox in the Inspector:

爲了更好地使用靜態批處理,你需要明確指出哪些物體是靜止的,並且在遊戲中永遠不會移動、旋轉和縮放。想完成這一步,你只需要在檢測器(Inspector)中將Static複選框打勾即可,如下圖所示:

 

Using static batching will require additional memory for storing the combined geometry. If several objects shared the same geometry before static batching, then a copy of geometry will be created for each object, either in the Editor or at runtime. This might not always be a good idea - sometimes you will have to sacrifice rendering performance by avoiding static batching for some objects to keep a smaller memory footprint. For example, marking trees as static in a dense forest level can have serious memory impact.

使用靜態批處理操作需要額外的內存開銷來儲存合併後的幾何數據。在靜態批處理之前,如果一些物體共用了同樣的幾何數據,那麼引擎會在編輯以及運行狀態對每個物體創建一個幾何數據的備份。這並不總是一個好的想法,因爲有時候,你將不得不犧牲一點渲染性能來防止一些物體的靜態批處理,從而保持較少的內存開銷。比如,將濃密森裏中樹設爲Static,會導致嚴重的內存開銷。

 

Static batching is only available in Unity iOS Advanced.

靜態批處理目前只支持Unity iOS Advanced。

 

 

備註:最近一直在研究Unity3D的性能優化問題,這段時間可能會多翻譯這方面的文章。





前兩天,MadFinger,就是當今iOS與Android上畫質最牛逼閃閃的遊戲之一——ShadowGun的開發商,令人驚異地放出了一個ShadowGun的樣例關卡以及若干可免費使用的Shader,國外同行們的分享精神真的是令人讚歎不已。原文在這裏,以下是我的一些摘錄和筆記。

首先是一些優化常識。針對圖形方面的優化主要包括三角形數量,紋理所佔內存,以及Shader,前兩項基本沒什麼好講的,針對設備機能的限制制定相應的指標即可,所以Shader就成爲了圖形性能優化的關鍵。

Alpha blending

在Unity官方文檔中講,由於硬件原因,在iOS設備上使用alpha-test會造成很大的性能開銷,應儘量使用alpha-blend代替。這裏提到,在同屏使用alpha-blend的面數,尤其是這些面所佔屏幕面積的大小,對性能也會造成很大影響。原因是使用alpha-blend的面會造成overdraw的增加,這尤其對低性能設備的影響很大。不過沒有購買Pro版,沒有Occlusion Culling功能的話,就不必顧慮這一問題了,反正overdraw是必然的。

複雜的Per-pixel shader

Per-pixel shader即Fragment shader,顧名思義是要對每個渲染到屏幕上的像素做處理的shader,如果per-pixel shader比較複雜且需要處理的像素很多時,也就是使用該shader的面佔屏幕面積很大時,對性能的影響甚至要超過alpha blending。因此複雜的per-pixel shader只適用於小物體。

下面是對幾個Shader的逐一講解:

Environment specular maps(Shader Virtual Gloss Per Vertex Additive)

Specular map通常都是利用貼圖的alpha通道來定義物體表面的光滑程度(反光度),這個shader的特點是per-vertex計算反光度的,有着相當不錯的效果的同時比per-pixel的shader性能要高得多。這個shader很適用於關卡環境等佔很大區域的模型。

經過優化的動態角色光照和陰影(Light probes和BRDF Shader)

傳統的Lightmaps無法支持動態物體,對此Unity提供了Light probes技術,預先把動態物體的光照信息保存在代理對象(即Light probes)中,運行時動態物體從距離最近的Probe中獲取光照信息。

Unity本身還提供了一個效果非常棒的專爲移動設備優化過的角色Shader,支持Diffuse、Specular和Normal maps,並通過一個特殊的腳本生成貼圖用於模仿BRDF光照效果。最終產生的效果堪比次時代大作中的角色光影效果。

霧和體積光(Shader Blinking Godrays)

目前在移動設備上要開啓真正的霧效基本不可行,ShadowGun的方案是通過簡單的網格+透明貼圖(稱爲霧面)來模擬霧效。在玩家靠近時,霧面逐漸變淡,同時fog plane的頂點也會移開(即使完全透明的alpha面也會消耗很多渲染時間)。

使用這個Shader的網格需要經過處理:

  • 頂點的alpha值用於決定頂點是否可以移動(在例子中0爲不可動,1爲可動)。
  • 頂點法線決定移動的方向
  • 然後Shader通過計算與觀察者的距離來控制霧面的淡入/淡出。

這個Shader還可以用來做體積光和其它一些alpha效果。

飛機墜毀的濃煙效果(Shader Scroll 2 Layers Sine Alpha-blended)

通過粒子產生濃煙的代價太高,所以ShadowGun中使用了網格+貼圖動畫來製作這個效果。通過混合兩層貼圖並讓它們交錯移動來產生動畫效果。其中頂點alpha值用於讓網格的邊緣看起來比較柔和,同時使用頂點顏色來模擬從火焰到煙霧的過渡效果。

帶動態效果的天空盒(Shader Scroll 2 Layers Multiplicative)

通過兩張貼圖的混合和移動產生雲的動態效果。

旗幟和衣服的飄動效果(Shader Lightmap + Wind)

同樣利用頂點alpha值決定哪些頂點可以移動,然後shader的參數用於調整擺動的方向和速度。


http://blog.sina.com.cn/s/blog_3d886e670101qpb6.html

發佈了9 篇原創文章 · 獲贊 23 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章