Animoji已在上線,第一個版本完成了表情跟蹤记某、模型變換矩陣動態(tài)更新畅姊、光照優(yōu)化壤追、骨骼動畫與關鍵變形動畫等技術贿堰,但Animoji模型仍然存在兩個凸出的問題:
- 模型體積大纵搁。如BabyQ有23M(包括靜態(tài)模型、表情、動畫热押、貼圖)抡驼,壓縮后仍有8.7M。
- 表面不光滑。在模型做表情時,會出現(xiàn)坑坑洼洼的情況,最終只能靠光照和貼圖優(yōu)化來彌補,但導致表情不夠生動胚想。
- 色彩不夠真實琐凭。由于過度依賴貼圖彌補上一個不足,模型無法實現(xiàn)動態(tài)的真實陰影與色彩變化浊服。
針對在三個問題统屈,本期對黃臉、多福兩個模型使用了新的優(yōu)化方案牙躺。
優(yōu)化結果
體積優(yōu)化結果
表情與靜態(tài)文件減小85.2%愁憔,同時得益于動態(tài)增加三角面的技術,貼圖也減小了78.4%孽拷。
除此以外吨掌,由于優(yōu)化了變形動畫的存儲方式與模型文件的壓縮方式,使得表情文件的大小在上面的基礎上減小了30%脓恕。
最終多福的壓縮后體積為1.9M(多福的貼圖會多一些)膜宋,而之前BabyQ的壓縮后體積是8.7M。
模型表面優(yōu)化結果
由于哈士奇的模型表情光滑炼幔,在方向光源下產(chǎn)生的陰影非常尷尬秋茫,最后不得不是用高強度點光源與陰影貼圖(在貼圖上畫假陰影)、法線貼圖這么一套復雜的組合消除這些尷尬的陰影乃秀。
多福采用了幾何著色器肛著,在渲染管線圖元裝配之后圆兵,動態(tài)增添三角面以獲得更加光滑的表面。由于這種方法在渲染管線的前端就改造了幾何體枢贿,所以不影響骨骼動畫和后續(xù)的著色殉农,能夠兼容現(xiàn)有的實現(xiàn)方案。
視覺效果優(yōu)化結果
對比蘋果的Animoji局荚,我們發(fā)現(xiàn)统抬,即使在我們獲得了光滑的模型,做表情時依然有顏色死板的感覺危队,視覺上質(zhì)感不夠,動畫形象不夠靈動钙畔。
研究發(fā)現(xiàn)茫陆,蘋果應該是在shader中加入了fresnel效果,同時在shader中放大了光照的影響面積擎析。依此我在shader中加入了這些效果簿盅,并依據(jù)反射和環(huán)境光遮擋貼圖,實現(xiàn)了令人舒適的視覺效果揍魂。
技術方案
動態(tài)細分技術
在Vertex shader與Fragment shader之間桨醋,現(xiàn)代的GPU渲染管線加入了可選的Geometry shader,與Vertex shader不同现斋,它是處理頂點集合喜最,即圖元。他最大的優(yōu)勢是庄蹋,它不僅可以更改頂點瞬内,還可以增加頂點,由此三角面會增加限书。
由于它在管線中的位置處于光柵化之前虫蝶,所以它對模型的處理實際上是在渲染之前就已經(jīng)完成,所以可以和已有的變形倦西、骨骼動畫相兼容能真。
普通的分段由于是在渲染管線末端才增加三角面,會導致變形動畫Crash的問題(頂點不一致)扰柠,這也是哈士奇無法一直無法光滑的原因粉铐。
得益于動態(tài)細分,下發(fā)的模型可以是非常粗糙的幾何體耻矮,也因此模型大小大大減小秦躯。
但使用Geometry shader細分網(wǎng)格時,若細分算法選擇不佳裆装,依舊會導致模型即使增加三角面后依然不夠光滑踱承。
使用合適的細分算法
原始網(wǎng)格如下倡缠,若使用蘋果默認的Catmull-Clark算法,則結果會導致細分后的頂點分布位置茎活,導致三角面分布不均勻昙沦。
Catmull-Clark算法結果如下。
由于Catmull-Clark算法在增加頂點與邊后载荔,會依據(jù)他們計算原有頂點的新位置盾饮。
其中F為新增加的面頂點,R為新增加的邊中點懒熙,n為相鄰邊數(shù)丘损,P為原有頂點位置。計算得到的就是新位置工扎,這在靜態(tài)模型上沒有問題徘钥,但是在有動畫的模型上,由于三角面不均勻肢娘,依然會導致不光滑呈础。
而我想要的結果是三角面均勻分布的細分方案,即網(wǎng)格邊二分法的效果橱健,如下
遺憾的是蘋果并不提供細分算法的選擇而钞,大量實踐發(fā)現(xiàn),若使用Maya導出的模型是四邊面拘荡,雖然蘋果依然會將網(wǎng)格三角面化臼节,但在細分時會選擇邊二分法,這也是個比較Trick的方法珊皿,在最新的iOS beta也有效官疲。
動態(tài)細分后貼圖的優(yōu)勢
由于模型已經(jīng)做到了動態(tài)細分,那么貼圖可以選擇低質(zhì)量的圖片亮隙。之前是因為需要在貼圖上畫出細膩的陰影途凫,所以不能過渡壓縮導致模糊。而新模型是使用動態(tài)陰影的溢吻,所以貼圖壓縮78.4%维费,且理論上可以進一步壓縮。
自適應與性能提高
動態(tài)細分能做到在管線光柵化之前就增加三角面促王,但這意味每一次渲染都會使用Geometry shader犀盟。
而對比發(fā)現(xiàn)GPU的使用率提高了9%左右,GPU的性能雖然可以做到無壓力蝇狼,但進一步的優(yōu)化也不是無意義的阅畴。
由于透視造成的近大遠小,那么離我們較遠時迅耘,細分可以盡量低級贱枣,在很近是监署,可以盡量高級。依據(jù)此思想纽哥,我加入了自適應的細分能力來降低性能損耗钠乏。
同時我考慮在模型不使用時,將細分后的模型保存下來春塌,第二次使用時就不用在渲染管線中每一次都細分晓避,但這個需要進一步測試,因為細分后的模型數(shù)據(jù)量大大增加只壳,對內(nèi)存和CPU又會有挑戰(zhàn)俏拱,如何將CPU與GPU做一個均衡優(yōu)化也一直是個難題,這里后期跟進吼句。
Shader著色優(yōu)化
fresnel效果
fresnel效果能夠產(chǎn)生更合理的反射效果彰触,在渲染時會將不同材質(zhì)的折射考慮進去。強烈的反射會讓材質(zhì)顯得更通透命辖。
數(shù)學公式不復述了,即代碼實現(xiàn)了fresnel公式分蓖。
float Mask = _surface.specular.b;
float basis = saturate(dot(_surface.view, _surface.normal));
float fresnel = saturate(pow(1.-fresnelBasis , edgeDark_rimLight.w)) * pow(AO,5.0);
float fresnelDarkening = saturate(pow(1.-fresnelBasis , edgeDark_rimLight.y)) * pow(AO,5.0);
vec3 darkeningcolor = EdgesDarkeningColor.rgb;
color = mix(color, color * darkeningcolor, fresnelDarkening * lightWrapMask * edgeDark_rimLight.x);
改善光照效果
使用AO貼圖和反射貼圖尔艇,營造更柔和的光照效果。這里使用了常用的圖形模糊掩膜么鹤,讓反射效果更柔和终娃。
vec4 Wrap = vec4(0,0,0,0);
float diffuse = saturate(dot(_surface.normal, _light.direction) );
diffuse = pow(diffuse, Wrap.x + 1);
vec3 halfVec = normalize(_light.direction + _surface.view);
// the roughness texture control the shininess :
float roughness = _surface.specular.g;
float shininess = mix (_surface.shininess, 15.0 + Wrap.z, roughness);
vec3 specular = pow(max(dot(_surface.normal, halfVec), 0.0), 0.1);
specular =max(dot(_surface.normal, halfVec), 0.0);
specular = pow(specular, shininess) * _light.intensity.rgb;
specular *= _surface.specular.r;
_lightingContribution.diffuse.rgb = _light.intensity.rgb * diffuse * (1.4 + Wrap.y)+ _lightingContribution.ambient;
_lightingContribution.specular = specular;
總結
Animoji模型優(yōu)化后達到了工業(yè)級使用的級別,但渲染方案依然有優(yōu)化的空間蒸甜,而跨平臺的實現(xiàn)方案亦在觀察之列棠耕。
圖形學博大精深,我時常感覺自己像個小學生對面對的問題不知所措柠新,時常對相關知識閱讀三遍也不能理解∏嫌現(xiàn)在我也只是了解了其中冰山一角,工作就像求學恨憎,得保持對未知的好奇和渴望蕊退,同時要保有敬畏之心。