在Unity3D中瑞眼,常用的減少Draw call的優(yōu)化技術就是批處理技術吗铐。批處理的原理是減少每一幀需要的Draw call數目息楔。為了把一個對象渲染到屏幕上妒峦,CPU需要檢查哪些光源影響了該物體重斑,綁定shader并設置它的參數,再把渲染命令發(fā)送給GPU肯骇。當場景中包含了大量的對象時窥浪,這些操作就會非常耗時。例如笛丙,如果我們需要渲染一千個三角形漾脂,把它們按一千個單獨的網格進行渲染所花費的時間要遠遠大于渲染一個包含一千個三角形的網格。在這兩種情況下胚鸯,GPU的性能消耗其實并沒有多大的區(qū)別符相,但CPU的draw call數目就會成為性能瓶頸。因此蠢琳,批處理的思想很簡單啊终,就是在每次調用draw call時盡可能多地處理多個物體。
使用同一材質的物體可以進行批處理傲须,因為對于使用同一材質的物體蓝牲,它們之間的不同僅僅在于頂點數據的差別。我們可以把這些頂點數據合并在一起泰讽,再一起發(fā)送給GPU例衍,這樣就可以完成一次批處理。
在Unity中支持兩種類型的批處理已卸,一種是動態(tài)批處理佛玄,另一種是靜態(tài)批處理。對于動態(tài)批處理來說累澡,優(yōu)點是一切處理Unity自動完成的梦抢,不需要我們做任何操作,而且物體可以是移動的愧哟,缺點是限制有很多奥吩,可能一不小心就會破壞了這種機制哼蛆,導致Unity無法動態(tài)批處理一些使用了相同材質的物體。而對于靜態(tài)批處理來說霞赫,它的優(yōu)點是自由度很高腮介,限制很少;但缺點是可能會占用更多的內存端衰,而經過靜態(tài)批處理后的所有物體都不可以再移動了(即便在腳本中嘗試改變物體的位置也是無效的)叠洗。
Unity3D中的動態(tài)批處理技術
動態(tài)批處理的原理是,每一幀把可以進行批處理的模型網格進行合并旅东,再把合并后的模型數據傳遞給GPU灭抑,然后使用同一個材質對其渲染。除了實現方便玉锌,動態(tài)批處理的另一個好處就是名挥,經過批處理的物體仍然可以移動疟羹,這是由于在處理每幀時Unity都會重新合并一次網格主守。
雖然Unity的動態(tài)批處理不需要我們進行任何額外工作,但只有滿足條件的模型和材質才可以被動態(tài)批處理榄融。(需要注意的是参淫,隨著Unity版本的變化,這些條件也有一些改變)這些條件限制是:
1.能夠進行動態(tài)批處理的網格的頂點屬性規(guī)模要小于900.例如愧杯,如果shader中需要使用頂點位置涎才,法線和紋理坐標這三個頂點屬性,那么要想讓模型能夠被動態(tài)批處理力九,它的頂點數目不能超過300.需要注意的是耍铜,這個數字在未來有可能會發(fā)生變化,因此不要依賴這個數據跌前。
2.一般來說棕兼,所有對象都需要使用同一縮放尺度(可以是(1,1抵乓,1)伴挚,(1,2灾炭,3)茎芋,(1.5,1.4蜈出,1.3)等田弥,但必須都一樣)。一個例外情況是铡原,如果所有的物體都是用了不同的非統(tǒng)一縮放皱蹦,那么他們也是可以被動態(tài)批處理的煤杀。但在Unity5中,這種對模型縮放的限制已經不存在了沪哺。
3.對于使用光照貼圖紋理的物體需要小心處理沈自。這些物體需要額外的渲染參數,例如辜妓,在光照貼圖紋理上的索引和偏移量以及縮放信息等因此枯途,為了讓這些物體可以被動態(tài)批處理,我們需要保證它們指向光照貼圖紋理中的同一個位置籍滴。
4.有多個Pass通道的shader會中斷批處理酪夷。在前向渲染中,我們有時需要使用額外的Pass來為模型添加更多的光照效果孽惰,但這樣一來模型就不會被動態(tài)批處理了晚岭。
Unity3D中的靜態(tài)批處理技術
靜態(tài)批處理的實現原理是,只在運行的開始階段勋功,把需要進行靜態(tài)批處理的模型合并到一個新的網格結構中坦报,這意味著這些模型不可以在運行時刻被移動。但由于它只需要進行一次合并操作狂鞋,因此比動態(tài)批處理更加高效片择。但靜態(tài)批處理的缺點是需要占用更多的內存來存儲合并后的幾何結構。這時因為骚揍,如果在靜態(tài)批處理前一些物體共享了相同的網格字管,那么在內存中每一個物體都會對應一個該網格的復制品,即一個網格會變成多個網格再發(fā)送給GPU信不。如果這類使用相同網格的對象很多嘲叔,那么這就會成為一種性能瓶頸了。例如抽活,如果在一個使用了1000個相同樹模型的森林中使用靜態(tài)批處理硫戈,那么就會多使用1000倍的內存,這會造成嚴重的內存影響酌壕。這時的解決方法就是要么忍受這種犧牲內存換取性能的方法掏愁,要么不要使用靜態(tài)批處理,而使用動態(tài)批處理技術(但要小心控制模型的頂點屬性數目)卵牍,或者自己編寫批處理方法果港。
在Unity中使用靜態(tài)批處理的方法是,在場景中選中需要靜態(tài)批處理的物體糊昙,在其Inspector面板的右上角勾選上Batching static靜態(tài)屬性辛掠。在內部實現上,Unity首先把這些靜態(tài)物體變換到世界空間下,然后為它們構建一個更大的頂點和索引緩存萝衩,對于使用同一材質的物體回挽。Unity只需要調用一個drawcall就可以繪制全部物體。而對于使用不同材質的物體猩谊,靜態(tài)批處理同樣可以提升渲染性能千劈。盡管這些物體仍然需要調用多個draw call,但靜態(tài)批處理可以減少這些draw call之間的狀態(tài)切換牌捷,而這些切換往往是費時的操作墙牌。
我們可以在Unity的分析器中觀察到應用靜態(tài)批處理前后VBO total(Vertex Buffer Object,頂點緩存對象)的變化暗甥。在一些物體共享了相同的網格的情況下喜滨,我們可以看到這些物體在使用了靜態(tài)批處理技術后,VBO total的數目變大了撤防,這正是因為靜態(tài)批處理會占用更多內存的緣故虽风。正如上面所講,靜態(tài)批處理需要占用更多的內存來存儲合并后的幾何結構寄月,如果一些物體共享了相同的網格辜膝,那么在內存中每個物體都會對應一個該網格的復制品。
如果場景中包含了除了平行光以外的其他光源剥懒,并且在Shader中定義了額外的Pass來處理它們内舟,這些額外的Pass部分是不會被批處理的合敦,但是處理平行光的Base Pass部分仍然會被靜態(tài)批處理初橘。
Unity3D中使用共享材質
無論是動態(tài)批處理還是靜態(tài)批處理,都要求模型之間需要共享同一個材質充岛。但不同的模型之間總會需要有不同的渲染屬性保檐,例如,使用不同的紋理崔梗,顏色等夜只。這時我們需要一些策略來盡可能的合并材質。
如果兩個材質之間只有使用的紋理不同蒜魄,我們可以把這些紋理合并到一張更大的紋理中扔亥,這張更大紋理被稱為是一張圖集(atlas)。一旦使用了同一紋理谈为,我們就可以使用同一材質旅挤,再使用不同的采樣坐標對紋理采樣即可。
但有時除了紋理不同外伞鲫,不同的物體在材質上還有一些微小的參數變化粘茄,例如,顏色不同,某些浮點屬性不同柒瓣。但是儒搭,不管是動態(tài)批處理還是靜態(tài)批處理,它們的前提都是使用同一個材質芙贫。是同一個搂鲫,而不是使用了同一shader的材質,也就是說它們指向的材質必須是同一個實體磺平。這意味著默穴,只要我們調整了參數,就會影響到所欲使用這個材質的對象褪秀。那么想要微小的調整怎么辦呢蓄诽?一種常用的方法就是使用網格的頂點數據來存儲這些參數(最常見的就是頂點顏色數據)。
經過批處理后的物體會被處理成更大的VBO發(fā)送給GPU媒吗,VBO中的數據可以作為輸入傳遞給頂點著色器仑氛,因此,我們可以巧妙地對VBO中的數據進行控制闸英,從而達到不同效果的目的锯岖。一個例子就是,森林場景中的所有樹使用了同一材質甫何,我們希望它們可以通過批處理來減少draw call出吹,但不同樹的顏色可能不同。這時辙喂,我們可以利用網格頂點的顏色數據來調整捶牢。
需要注意的是,如果我們需要在腳本中訪問共享材質巍耗,應該使用Renderer.sharedMaterial來保證修改的是和其他物體共享的材質秋麸,但這意味著修改會應用到所有使用該材質的物體上。另一個類似的API是Renderer.material炬太,如果使用Renderer.material來修改材質灸蟆,Unity會創(chuàng)建一個該材質的復制品,從而破壞批處理在該物體上的應用亲族。
關于在Unity3D中使用批處理的注意事項:
1.盡可能的使用靜態(tài)批處理炒考,但要時刻小心對內存的消耗,并且記住經過靜態(tài)批處理的物體不可以再被移動霎迫。
2.如果無法進行靜態(tài)批處理斋枢,而要使用動態(tài)批處理的話,那么盡可能減少物體的數目并且讓這些物體包含少量的頂點屬性和頂點數目女气。
3.對于游戲中的小道具杏慰,例如可以撿拾的金幣等,可以使用動態(tài)批處理。
4.對于動畫的這類物體缘滥,我們無法全部使用靜態(tài)批處理轰胁,但其中如果有不動的部分,可以把這部分標識成“Static”朝扼。
5.由于批處理需要把模型變換到世界空間下再合并它們赃阀,因此,如果shader中存在一些基于模型空間下坐標的運算擎颖,那么往往會得到錯誤的結果榛斯。一個解決方法是,在shader中使用DisableBatching標簽強制使該shader的材質不會被批處理搂捧。
6.使用半透明材質的物體通常需要使用嚴格的從后往前的繪制順序來保證透明混合的正確性驮俗。這意味著,當繪制順序無法滿足時允跑,批處理無法在這些物體上被成功地應用王凑。