
- 類型:濾鏡插件大。7.6M語言:中文 評分:6.6
- 標(biāo)簽:
用過Away3D的朋友估計(jì)都會發(fā)現(xiàn),在Away3D里面使用超過一定骨骼數(shù)量的角色,當(dāng)場景里面角色的數(shù)量稍微多一點(diǎn),整個場景就會很卡。
對于這個現(xiàn)象,我之前得出的結(jié)論是。Stage3D的VC緩存器數(shù)量的限制,造成了對需要占用VC的骨骼信息有限制。對于超過了限制數(shù)量的骨骼部分,Stage3D會把數(shù)據(jù)退回CPU計(jì)算。
這里存在幾個誤區(qū):
1、退回CPU的處理不是Stage3D做的,而是away3D本身做的。
原生的Stage3D對于超過能允許數(shù)量的骨骼,因?yàn)槌隽?28個vc,不會做其他處理,只會直接報錯:
ArgumentError: Error #3615: AGAL 驗(yàn)證失敗: 程序大小小于 程序的最小長度。
2、不是部分的退回,是通過一個開關(guān)判斷是否需要退回,全部退回。
開關(guān)是變量usesCPU。一開始給材質(zhì)賦值的時候,會判斷該模型是否需要退回cpu計(jì)算。假如不需要,就全部推到GPU計(jì)算,即使沒有動畫信息的時候,頂點(diǎn)著色器也會使用蒙皮計(jì)算的一套。假如需要退回cpu計(jì)算,那么就不會再使用蒙皮動畫的頂點(diǎn)程序,而直接用最基礎(chǔ)的頂點(diǎn)程序計(jì)算。
在明白了這兩點(diǎn)之后,看看Away3D對這個是否超出長度的功能做了什么處理:
1、通過對AnimationSetBase.cancelGPUCompatibility斷點(diǎn),發(fā)現(xiàn)了在SkeletonAnimator.testGPUCompatibility方法里面有檢查是否需要退回CPU的判斷。其判斷的條件是:
if (!_useCondensedIndices && (_forceCPU || _jointsPerVertex > 4 || pass.numUsedVertexConstants + _numJoints * 3 > 128))
可以看出:
除了_useCondensedIndices ==false,還需要
1._forceCPU == true
2.一個頂點(diǎn)受到大于4個骨骼的影響。
因?yàn)槊總va只能存xyzw四個數(shù),按照Away3D的頂點(diǎn)著色器的處理,就只能最多一個頂點(diǎn)受到4根骨骼的影響。
3.已經(jīng)使用的Vc,加上骨骼占用的VC,要少于128個。
由于Away對于骨骼 Transform推入GPU的計(jì)算是三個基向量,也就是占用3個緩存器,所以需要 骨骼數(shù)*3
后兩個條件,出現(xiàn)了優(yōu)化的空間:
首先,一般頂點(diǎn)最多受到3根骨骼影響已經(jīng)很足夠了。超過4根的信息可以考慮判斷其影響大小,將超出的而且權(quán)重小的部分排除掉。
然后,可以考慮一下怎樣減少輸入的vc數(shù)量,把三個基向量看有沒有辦法變成2個四維向量分別傳入位移和旋轉(zhuǎn)信息。由于Away3D使用的md5動畫格式本身就沒有導(dǎo)出縮放的,所以在沒有自己再寫解析器的情況下,沒有必要處理縮放的信息。
2、對于沒有超出允許范圍的情況,Away3D會通過代碼解析器組成頂點(diǎn)程序,然后每幀推入骨骼的三個基向量給agal計(jì)算。
在SkeletonAnimator.setRenderState方法里面,把計(jì)算出的所有骨骼的信息(_globalMatrices)傳入GPU,_numJoints是骨骼的數(shù)量。vertexConstantOffset是VC偏移量。也就是說,在vertexConstantOffset之前是其他頂點(diǎn)程序需要的VC,從vertexConstantOffset開始往后的所有VC都是骨骼信息使用的。由于每根骨骼有三個基向量,所以是_numJoints*3。
所以最終推入GPU的數(shù)據(jù)是這樣的:
stage3DProxy._context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, vertexConstantOffset, _globalMatrices, _numJoints*3);
3、對于返回cpu的情況,頂點(diǎn)程序是最簡單的m44 op,va0,vc0,不需要推送骨骼信息進(jìn)入vc,
而是在SkeletonAnimator.setRenderState方法里面判斷了if (_animationSet.usesCPU),
然后在SkeletonAnimator.morphGeometry方法里面在CPU模擬了一次GPU里面逐個頂點(diǎn)分別乘以受到影響的骨骼的基向量再乘以權(quán)重最后相加的過程,求出了每個頂點(diǎn)在每一幀實(shí)際的位置坐標(biāo),然后返回。
如果角色多、頂點(diǎn)多的情況下,這個過程在CPU算明顯是超級大的負(fù)荷,難怪幾個人物就卡死了。
這里又出現(xiàn)了優(yōu)化點(diǎn)了,寧愿對美術(shù)資源進(jìn)行限制,也不要用CPU來渲染。Away3D這個功能明顯是雞肋,只是為了做得全面,適應(yīng)各種沒有限制的模型,沒有項(xiàng)目可行性。
Away3D骨骼優(yōu)化的多種嘗試及結(jié)果
之前針對stage3d支持骨骼數(shù)量的優(yōu)化方案,做出過2個可能性的分析:
1、減少vc的推送,把transform拆分成一個四元數(shù)和一個三維向量。
2、拆分模型網(wǎng)格,把超出的部分拆分后分別推送。
之后的這段時間,我對這兩種方法都做出了嘗試。接下來談一下結(jié)論:
第一種方法:
通過向VC推送四元數(shù)和三維向量,結(jié)合骨骼的bindpose矩陣,是可以算出頂點(diǎn)在動畫之后的位置。之前有位朋友評論說在agal里面不能用四元數(shù)計(jì)算。這個說法不能算錯,因?yàn)閍gal的確沒有提供直接的四元數(shù)計(jì)算。但假如對3d圖形數(shù)學(xué)熟悉的朋友,就可以直接把運(yùn)算的公式在agal里面重現(xiàn)一下,就可以了。
這種做法的優(yōu)點(diǎn)是cpu計(jì)算實(shí)在很少,比如在解析動畫之后可以直接把動畫關(guān)鍵幀保存成四元數(shù)和三維向量。然后需要計(jì)算動畫的時候,直接獲取然后推送就行了。但缺點(diǎn)是公式在agal里面運(yùn)算會比較麻煩,一條點(diǎn)積的公式,在agal里面就需要拆分成好幾行。而agal是有限制的,不能超過200行。我嘗試的每個點(diǎn)受4根骨骼影響的情況,生成的agal代碼已經(jīng)有192行了。這是比較危險的情況了。假如我們的代碼還需要做其他的處理,那么行數(shù)可能就不夠了。
第二種方法:
我進(jìn)行的嘗試是把各個蒙皮模型的子模型的信息進(jìn)行提取,獲取到該網(wǎng)格實(shí)際受到哪些骨骼的影響,然后推送vc的時候,只推受到影響的骨骼。
這樣做是從一個側(cè)面的解決這個問題。我們做人物模型的時候就不能整個模型合并完再一起蒙皮,必須單獨(dú)逐個部分的做,然后每個部分只蒙受到影響的骨骼。
這樣做的結(jié)果是只要每個部位蒙皮的骨骼不超過一定量,整個人物就可以支持很多很多的骨骼。
不過這樣做也是有缺點(diǎn)的。由于是把模型拆分了,所以本來每個人物每一幀只需要獲取一次的骨骼信息,就變成要獲取多次了。這樣就變成加大了cpu的運(yùn)算量。而且由于模型數(shù)量多了,渲染的實(shí)際次數(shù)也多了。
對于這種情況,我稍微優(yōu)化了一下提取信息的方法,讓他還是同一個角色同一幀只獲取一次骨骼信息。
在沒優(yōu)化之前,由于超出骨骼數(shù)量會退回cpu運(yùn)算,一個2000多面的角色帶有40多根骨骼,away3d只能同屏運(yùn)算10個左右就掉幀了,F(xiàn)在同樣的面數(shù)和骨骼的人物,可以支持同屏80-90個左右而保持在30幀。
對于這個優(yōu)化結(jié)果,我覺得還是不能實(shí)際的應(yīng)用在項(xiàng)目中;蛘呶疫是需要在很多地方找一下優(yōu)化的可能性。因?yàn)樵跊]有優(yōu)化之前,對于2000多面的角色,但骨骼減少到20多根的情況下,同屏是可以跑100個而保持30幀的。如果是湊合著來做項(xiàng)目的情況下,其實(shí)直接讓美工減少骨骼數(shù)量會是更快的解決方法。不過這樣做,游戲的效果就被限制得很厲害了,很多好看的效果和服裝就表現(xiàn)不出來了。