Unity中的靜態(tài)批處理和動(dòng)態(tài)批處理操作
前言
Unity在運(yùn)行時(shí)可以將一些物體進(jìn)行合并,從而用一個(gè)繪制調(diào)用來(lái)渲染他們。這一操作,我們稱(chēng)之為“批處理”,能得到越好的渲染性能。
Unity中內(nèi)建的批處理機(jī)制所達(dá)到的效果要明顯強(qiáng)于使用幾何建模工具的批處理效果,因?yàn)?,Unity引擎的批處理操作是在物體的可視裁剪操作之后進(jìn)行的,處理的幾何信息少很多。
材質(zhì)
只有擁有相同材質(zhì)的物體才可以進(jìn)行批處理,因此,你需在程序中盡可能多地復(fù)用材質(zhì)。如果你的兩個(gè)材質(zhì)僅僅是紋理不同,那么你可通過(guò)紋理拼合來(lái)將這兩張紋理拼合成一張大的紋理,這樣,你就可以使用這個(gè)單一材質(zhì)來(lái)替代之前的兩個(gè)材質(zhì)了。
如果你要通過(guò)腳本來(lái)訪(fǎng)問(wèn)復(fù)用材質(zhì)屬性,那么值得注意:改變Renderer.material將會(huì)造成一份材質(zhì)的拷貝,因此,你應(yīng)該使用Renderer.sharedMaterial來(lái)保證材質(zhì)的共享狀態(tài)。
動(dòng)態(tài)批處理
如果動(dòng)態(tài)物體共用著相同的材質(zhì),那么Unity會(huì)自動(dòng)對(duì)這些物體進(jìn)行批處理,動(dòng)態(tài)批處理操作是自動(dòng)完成的,并不需要你進(jìn)行額外的操作。
Dynamic Batching啟用時(shí),Unity將嘗試自動(dòng)批量移動(dòng)物體到一個(gè)Draw Call中。要使物體可以被動(dòng)態(tài)批處理,它們應(yīng)該共享相同的材質(zhì),但是還有一些其他約束條件:
批處理動(dòng)態(tài)物體需要在每個(gè)頂點(diǎn)上進(jìn)行一定的開(kāi)銷(xiāo),所以動(dòng)態(tài)批處理僅支持小于900頂點(diǎn)的網(wǎng)格物體;如果你的著色器使用頂點(diǎn)位置,法線(xiàn)和UV值三種屬性,那么你只能批處理300頂點(diǎn)以下的物體;如果你的著色器需要使用頂點(diǎn)位置,法線(xiàn),UV0,UV1和切向量,那你只能批處理180頂點(diǎn)以下的物體。
盡量不要使用縮放尺度(scale)。分別擁有縮放尺度(1,1,1)和(2,2,2)的兩個(gè)物體將不會(huì)進(jìn)行批處理;統(tǒng)一縮放尺度的物體不會(huì)與非統(tǒng)一縮放尺度的物體進(jìn)行批處理。使用縮放尺度(1,1,1)和 (1,2,1)的兩個(gè)物體將不會(huì)進(jìn)行批處理,但是使用縮放尺度(1,2,1)和(1,3,1)的兩個(gè)物體將可以進(jìn)行批處理。
擁有光照貼圖的物體有其他渲染器參數(shù),例如光照貼圖索引或光照貼圖的偏移與縮放。一般來(lái)說(shuō),動(dòng)態(tài)光照貼圖的游戲?qū)ο髴?yīng)該指向完全相同的光照貼圖的位置。
多通道(Pass)的shader會(huì)妨礙批處理操作。比如,幾乎unity中所有的著色器在前向渲染中都支持多個(gè)光源,并為它們有效地開(kāi)辟多個(gè)通道,這就會(huì)要求額外的渲染次數(shù),所以繪制 “額外的每像素?zé)簟睍r(shí)不會(huì)被批處理。
靜態(tài)批處理
為了更好地使用靜態(tài)批處理,你需要明確指出哪些物體是靜止的,并且在游戲中永遠(yuǎn)不會(huì)移動(dòng)、旋轉(zhuǎn)和縮放。想完成這一步,你只需要在檢測(cè)器(Inspector)中將Static復(fù)選框打勾即可。只要這些物體不移動(dòng),并且擁有相同的材質(zhì)。因此,靜態(tài)批處理比動(dòng)態(tài)批處理更加有效,你應(yīng)該盡量低使用它,因?yàn)樗枰俚腃PU開(kāi)銷(xiāo)。
使用靜態(tài)批處理操作需要額外的內(nèi)存開(kāi)銷(xiāo)來(lái)儲(chǔ)存合并后的幾何數(shù)據(jù)。在靜態(tài)批處理之前,如果一些物體共用了同樣的幾何數(shù)據(jù),那么引擎會(huì)在編輯以及運(yùn)行狀態(tài)對(duì)每個(gè)物體創(chuàng)建一個(gè)幾何數(shù)據(jù)的備份。這并不總是一個(gè)好的想法,因?yàn)橛袝r(shí)候,將不得不犧牲一點(diǎn)渲染性能來(lái)防止一些物體的靜態(tài)批處理,從而保持較少的內(nèi)存開(kāi)銷(xiāo)。比如,將濃密森里中樹(shù)設(shè)為Static,會(huì)導(dǎo)致嚴(yán)重的內(nèi)存開(kāi)銷(xiāo)。這就是空間和時(shí)間上的相愛(ài)相殺。
最后,如果場(chǎng)景自帶靜態(tài)物體,會(huì)合并批次,這個(gè)大家都知道;如果靜態(tài)物體是場(chǎng)景加載后再讀取預(yù)制體動(dòng)態(tài)加載進(jìn)去的,就不會(huì)自動(dòng)合并批次,需要你加載完后調(diào)一下手動(dòng)合并批次的接口,StaticBatchingUtility.Combine 合并。
補(bǔ)充:在Unity3D中的渲染優(yōu)化-批處理技術(shù)
在Unity3D中,常用的減少Draw call的優(yōu)化技術(shù)就是批處理技術(shù)。批處理的原理是減少每一幀需要的Draw call數(shù)目。為了把一個(gè)對(duì)象渲染到屏幕上,CPU需要檢查哪些光源影響了該物體,綁定shader并設(shè)置它的參數(shù),再把渲染命令發(fā)送給GPU。當(dāng)場(chǎng)景中包含了大量的對(duì)象時(shí),這些操作就會(huì)非常耗時(shí)。例如,如果我們需要渲染一千個(gè)三角形,把它們按一千個(gè)單獨(dú)的網(wǎng)格進(jìn)行渲染所花費(fèi)的時(shí)間要遠(yuǎn)遠(yuǎn)大于渲染一個(gè)包含一千個(gè)三角形的網(wǎng)格。在這兩種情況下,GPU的性能消耗其實(shí)并沒(méi)有多大的區(qū)別,但CPU的draw call數(shù)目就會(huì)成為性能瓶頸。因此,批處理的思想很簡(jiǎn)單,就是在每次調(diào)用draw call時(shí)盡可能多地處理多個(gè)物體。
使用同一材質(zhì)的物體可以進(jìn)行批處理,因?yàn)閷?duì)于使用同一材質(zhì)的物體,它們之間的不同僅僅在于頂點(diǎn)數(shù)據(jù)的差別。我們可以把這些頂點(diǎn)數(shù)據(jù)合并在一起,再一起發(fā)送給GPU,這樣就可以完成一次批處理。
在Unity中支持兩種類(lèi)型的批處理,一種是動(dòng)態(tài)批處理,另一種是靜態(tài)批處理。對(duì)于動(dòng)態(tài)批處理來(lái)說(shuō),優(yōu)點(diǎn)是一切處理Unity自動(dòng)完成的,不需要我們做任何操作,而且物體可以是移動(dòng)的,缺點(diǎn)是限制有很多,可能一不小心就會(huì)破壞了這種機(jī)制,導(dǎo)致Unity無(wú)法動(dòng)態(tài)批處理一些使用了相同材質(zhì)的物體。而對(duì)于靜態(tài)批處理來(lái)說(shuō),它的優(yōu)點(diǎn)是自由度很高,限制很少;但缺點(diǎn)是可能會(huì)占用更多的內(nèi)存,而經(jīng)過(guò)靜態(tài)批處理后的所有物體都不可以再移動(dòng)了(即便在腳本中嘗試改變物體的位置也是無(wú)效的)。
Unity3D中的動(dòng)態(tài)批處理技術(shù)
動(dòng)態(tài)批處理的原理是,每一幀把可以進(jìn)行批處理的模型網(wǎng)格進(jìn)行合并,再把合并后的模型數(shù)據(jù)傳遞給GPU,然后使用同一個(gè)材質(zhì)對(duì)其渲染。除了實(shí)現(xiàn)方便,動(dòng)態(tài)批處理的另一個(gè)好處就是,經(jīng)過(guò)批處理的物體仍然可以移動(dòng),這是由于在處理每幀時(shí)Unity都會(huì)重新合并一次網(wǎng)格。
雖然Unity的動(dòng)態(tài)批處理不需要我們進(jìn)行任何額外工作,但只有滿(mǎn)足條件的模型和材質(zhì)才可以被動(dòng)態(tài)批處理。(需要注意的是,隨著Unity版本的變化,這些條件也有一些改變)這些條件限制是:
1.能夠進(jìn)行動(dòng)態(tài)批處理的網(wǎng)格的頂點(diǎn)屬性規(guī)模要小于900.例如,如果shader中需要使用頂點(diǎn)位置,法線(xiàn)和紋理坐標(biāo)這三個(gè)頂點(diǎn)屬性,那么要想讓模型能夠被動(dòng)態(tài)批處理,它的頂點(diǎn)數(shù)目不能超過(guò)300.需要注意的是,這個(gè)數(shù)字在未來(lái)有可能會(huì)發(fā)生變化,因此不要依賴(lài)這個(gè)數(shù)據(jù)。
2.一般來(lái)說(shuō),所有對(duì)象都需要使用同一縮放尺度(可以是(1,1,1),(1,2,3),(1.5,1.4,1.3)等,但必須都一樣)。一個(gè)例外情況是,如果所有的物體都是用了不同的非統(tǒng)一縮放,那么他們也是可以被動(dòng)態(tài)批處理的。但在Unity5中,這種對(duì)模型縮放的限制已經(jīng)不存在了。
3.對(duì)于使用光照貼圖紋理的物體需要小心處理。這些物體需要額外的渲染參數(shù),例如,在光照貼圖紋理上的索引和偏移量以及縮放信息等因此,為了讓這些物體可以被動(dòng)態(tài)批處理,我們需要保證它們指向光照貼圖紋理中的同一個(gè)位置。
4.有多個(gè)Pass通道的shader會(huì)中斷批處理。在前向渲染中,我們有時(shí)需要使用額外的Pass來(lái)為模型添加更多的光照效果,但這樣一來(lái)模型就不會(huì)被動(dòng)態(tài)批處理了。
Unity3D中的靜態(tài)批處理技術(shù)
靜態(tài)批處理的實(shí)現(xiàn)原理是,只在運(yùn)行的開(kāi)始階段,把需要進(jìn)行靜態(tài)批處理的模型合并到一個(gè)新的網(wǎng)格結(jié)構(gòu)中,這意味著這些模型不可以在運(yùn)行時(shí)刻被移動(dòng)。但由于它只需要進(jìn)行一次合并操作,因此比動(dòng)態(tài)批處理更加高效。但靜態(tài)批處理的缺點(diǎn)是需要占用更多的內(nèi)存來(lái)存儲(chǔ)合并后的幾何結(jié)構(gòu)。這時(shí)因?yàn)椋绻陟o態(tài)批處理前一些物體共享了相同的網(wǎng)格,那么在內(nèi)存中每一個(gè)物體都會(huì)對(duì)應(yīng)一個(gè)該網(wǎng)格的復(fù)制品,即一個(gè)網(wǎng)格會(huì)變成多個(gè)網(wǎng)格再發(fā)送給GPU。如果這類(lèi)使用相同網(wǎng)格的對(duì)象很多,那么這就會(huì)成為一種性能瓶頸了。例如,如果在一個(gè)使用了1000個(gè)相同樹(shù)模型的森林中使用靜態(tài)批處理,那么就會(huì)多使用1000倍的內(nèi)存,這會(huì)造成嚴(yán)重的內(nèi)存影響。這時(shí)的解決方法就是要么忍受這種犧牲內(nèi)存換取性能的方法,要么不要使用靜態(tài)批處理,而使用動(dòng)態(tài)批處理技術(shù)(但要小心控制模型的頂點(diǎn)屬性數(shù)目),或者自己編寫(xiě)批處理方法。
在Unity中使用靜態(tài)批處理的方法是,在場(chǎng)景中選中需要靜態(tài)批處理的物體,在其Inspector面板的右上角勾選上Batching static靜態(tài)屬性。在內(nèi)部實(shí)現(xiàn)上,Unity首先把這些靜態(tài)物體變換到世界空間下,然后為它們構(gòu)建一個(gè)更大的頂點(diǎn)和索引緩存,對(duì)于使用同一材質(zhì)的物體。Unity只需要調(diào)用一個(gè)drawcall就可以繪制全部物體。而對(duì)于使用不同材質(zhì)的物體,靜態(tài)批處理同樣可以提升渲染性能。盡管這些物體仍然需要調(diào)用多個(gè)draw call,但靜態(tài)批處理可以減少這些draw call之間的狀態(tài)切換,而這些切換往往是費(fèi)時(shí)的操作。
我們可以在Unity的分析器中觀(guān)察到應(yīng)用靜態(tài)批處理前后VBO total(Vertex Buffer Object,頂點(diǎn)緩存對(duì)象)的變化。在一些物體共享了相同的網(wǎng)格的情況下,我們可以看到這些物體在使用了靜態(tài)批處理技術(shù)后,VBO total的數(shù)目變大了,這正是因?yàn)殪o態(tài)批處理會(huì)占用更多內(nèi)存的緣故。正如上面所講,靜態(tài)批處理需要占用更多的內(nèi)存來(lái)存儲(chǔ)合并后的幾何結(jié)構(gòu),如果一些物體共享了相同的網(wǎng)格,那么在內(nèi)存中每個(gè)物體都會(huì)對(duì)應(yīng)一個(gè)該網(wǎng)格的復(fù)制品。
如果場(chǎng)景中包含了除了平行光以外的其他光源,并且在Shader中定義了額外的Pass來(lái)處理它們,這些額外的Pass部分是不會(huì)被批處理的,但是處理平行光的Base Pass部分仍然會(huì)被靜態(tài)批處理。
Unity3D中使用共享材質(zhì)
無(wú)論是動(dòng)態(tài)批處理還是靜態(tài)批處理,都要求模型之間需要共享同一個(gè)材質(zhì)。但不同的模型之間總會(huì)需要有不同的渲染屬性,例如,使用不同的紋理,顏色等。這時(shí)我們需要一些策略來(lái)盡可能的合并材質(zhì)。
如果兩個(gè)材質(zhì)之間只有使用的紋理不同,我們可以把這些紋理合并到一張更大的紋理中,這張更大紋理被稱(chēng)為是一張圖集(atlas)。一旦使用了同一紋理,我們就可以使用同一材質(zhì),再使用不同的采樣坐標(biāo)對(duì)紋理采樣即可。
但有時(shí)除了紋理不同外,不同的物體在材質(zhì)上還有一些微小的參數(shù)變化,例如,顏色不同,某些浮點(diǎn)屬性不同。但是,不管是動(dòng)態(tài)批處理還是靜態(tài)批處理,它們的前提都是使用同一個(gè)材質(zhì)。是同一個(gè),而不是使用了同一shader的材質(zhì),也就是說(shuō)它們指向的材質(zhì)必須是同一個(gè)實(shí)體。這意味著,只要我們調(diào)整了參數(shù),就會(huì)影響到所欲使用這個(gè)材質(zhì)的對(duì)象。那么想要微小的調(diào)整怎么辦呢?一種常用的方法就是使用網(wǎng)格的頂點(diǎn)數(shù)據(jù)來(lái)存儲(chǔ)這些參數(shù)(最常見(jiàn)的就是頂點(diǎn)顏色數(shù)據(jù))。
經(jīng)過(guò)批處理后的物體會(huì)被處理成更大的VBO發(fā)送給GPU,VBO中的數(shù)據(jù)可以作為輸入傳遞給頂點(diǎn)著色器,因此,我們可以巧妙地對(duì)VBO中的數(shù)據(jù)進(jìn)行控制,從而達(dá)到不同效果的目的。一個(gè)例子就是,森林場(chǎng)景中的所有樹(shù)使用了同一材質(zhì),我們希望它們可以通過(guò)批處理來(lái)減少draw call,但不同樹(shù)的顏色可能不同。這時(shí),我們可以利用網(wǎng)格頂點(diǎn)的顏色數(shù)據(jù)來(lái)調(diào)整。
需要注意的是,如果我們需要在腳本中訪(fǎng)問(wèn)共享材質(zhì),應(yīng)該使用Renderer.sharedMaterial來(lái)保證修改的是和其他物體共享的材質(zhì),但這意味著修改會(huì)應(yīng)用到所有使用該材質(zhì)的物體上。另一個(gè)類(lèi)似的API是Renderer.material,如果使用Renderer.material來(lái)修改材質(zhì),Unity會(huì)創(chuàng)建一個(gè)該材質(zhì)的復(fù)制品,從而破壞批處理在該物體上的應(yīng)用。
關(guān)于在Unity3D中使用批處理的注意事項(xiàng):
1.盡可能的使用靜態(tài)批處理,但要時(shí)刻小心對(duì)內(nèi)存的消耗,并且記住經(jīng)過(guò)靜態(tài)批處理的物體不可以再被移動(dòng)。
2.如果無(wú)法進(jìn)行靜態(tài)批處理,而要使用動(dòng)態(tài)批處理的話(huà),那么盡可能減少物體的數(shù)目并且讓這些物體包含少量的頂點(diǎn)屬性和頂點(diǎn)數(shù)目。
3.對(duì)于游戲中的小道具,例如可以撿拾的金幣等,可以使用動(dòng)態(tài)批處理。
4.對(duì)于動(dòng)畫(huà)的這類(lèi)物體,我們無(wú)法全部使用靜態(tài)批處理,但其中如果有不動(dòng)的部分,可以把這部分標(biāo)識(shí)成“Static”。
5.由于批處理需要把模型變換到世界空間下再合并它們,因此,如果shader中存在一些基于模型空間下坐標(biāo)的運(yùn)算,那么往往會(huì)得到錯(cuò)誤的結(jié)果。一個(gè)解決方法是,在shader中使用DisableBatching標(biāo)簽強(qiáng)制使該shader的材質(zhì)不會(huì)被批處理。
6.使用半透明材質(zhì)的物體通常需要使用嚴(yán)格的從后往前的繪制順序來(lái)保證透明混合的正確性。這意味著,當(dāng)繪制順序無(wú)法滿(mǎn)足時(shí),批處理無(wú)法在這些物體上被成功地應(yīng)用。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
C#使用HttpClient對(duì)大文件進(jìn)行斷點(diǎn)上傳和下載
這篇文章主要介紹了C#如何使用HttpClient對(duì)大文件進(jìn)行斷點(diǎn)上傳和下載,文章通過(guò)代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-06-06.NET單點(diǎn)登陸的實(shí)現(xiàn)方法及思路
這篇文章介紹了.NET單點(diǎn)登陸的實(shí)現(xiàn)方法及思路,有需要的朋友可以參考一下,希望對(duì)你有所幫助2013-07-07

C#并發(fā)容器之ConcurrentDictionary與普通Dictionary帶鎖性能詳解

C#算法之無(wú)重復(fù)字符的最長(zhǎng)子串