美文网首页iOS程序猿
Animoji模型优化方案总结

Animoji模型优化方案总结

作者: 皮皮Warrior | 来源:发表于2020-01-07 16:43 被阅读0次

    Animoji已在上线,第一个版本完成了表情跟踪、模型变换矩阵动态更新、光照优化、骨骼动画与关键变形动画等技术,但Animoji模型仍然存在两个凸出的问题:

    1. 模型体积大。如BabyQ有23M(包括静态模型、表情、动画、贴图),压缩后仍有8.7M。
    2. 表面不光滑。在模型做表情时,会出现坑坑洼洼的情况,最终只能靠光照和贴图优化来弥补,但导致表情不够生动。
    3. 色彩不够真实。由于过度依赖贴图弥补上一个不足,模型无法实现动态的真实阴影与色彩变化。

    针对在三个问题,本期对黄脸、多福两个模型使用了新的优化方案。

    优化结果

    体积优化结果

    优化后模型大小

    表情与静态文件减小85.2%,同时得益于动态增加三角面的技术,贴图也减小了78.4%。
    除此以外,由于优化了变形动画的存储方式与模型文件的压缩方式,使得表情文件的大小在上面的基础上减小了30%。
    最终多福的压缩后体积为1.9M(多福的贴图会多一些),而之前BabyQ的压缩后体积是8.7M。

    模型表面优化结果

    优化后模型

    由于哈士奇的模型表情光滑,在方向光源下产生的阴影非常尴尬,最后不得不是用高强度点光源与阴影贴图(在贴图上画假阴影)、法线贴图这么一套复杂的组合消除这些尴尬的阴影。
    多福采用了几何着色器,在渲染管线图元装配之后,动态增添三角面以获得更加光滑的表面。由于这种方法在渲染管线的前端就改造了几何体,所以不影响骨骼动画和后续的着色,能够兼容现有的实现方案。

    视觉效果优化结果

    对比苹果的Animoji,我们发现,即使在我们获得了光滑的模型,做表情时依然有颜色死板的感觉,视觉上质感不够,动画形象不够灵动。
    研究发现,苹果应该是在shader中加入了fresnel效果,同时在shader中放大了光照的影响面积。依此我在shader中加入了这些效果,并依据反射和环境光遮挡贴图,实现了令人舒适的视觉效果。

    添加shader后

    技术方案

    动态细分技术

    在Vertex shader与Fragment shader之间,现代的GPU渲染管线加入了可选的Geometry shader,与Vertex shader不同,它是处理顶点集合,即图元。他最大的优势是,它不仅可以更改顶点,还可以增加顶点,由此三角面会增加。

    由于它在管线中的位置处于光栅化之前,所以它对模型的处理实际上是在渲染之前就已经完成,所以可以和已有的变形、骨骼动画相兼容。

    普通的分段由于是在渲染管线末端才增加三角面,会导致变形动画Crash的问题(顶点不一致),这也是哈士奇无法一直无法光滑的原因。

    得益于动态细分,下发的模型可以是非常粗糙的几何体,也因此模型大小大大减小。


    渲染管线

    但使用Geometry shader细分网格时,若细分算法选择不佳,依旧会导致模型即使增加三角面后依然不够光滑。

    使用合适的细分算法

    原始网格如下,若使用苹果默认的Catmull-Clark算法,则结果会导致细分后的顶点分布位置,导致三角面分布不均匀。


    原始网格

    Catmull-Clark算法结果如下。


    Catmull-Clark算法细分后
    由于Catmull-Clark算法在增加顶点与边后,会依据他们计算原有顶点的新位置。 原始顶点的新位置

    其中F为新增加的面顶点,R为新增加的边中点,n为相邻边数,P为原有顶点位置。计算得到的就是新位置,这在静态模型上没有问题,但是在有动画的模型上,由于三角面不均匀,依然会导致不光滑。


    Catmull-Clark细分后仍不光滑

    而我想要的结果是三角面均匀分布的细分方案,即网格边二分法的效果,如下


    边二分法后的网格

    遗憾的是苹果并不提供细分算法的选择,大量实践发现,若使用Maya导出的模型是四边面,虽然苹果依然会将网格三角面化,但在细分时会选择边二分法,这也是个比较Trick的方法,在最新的iOS beta也有效。

    动态细分后贴图的优势

    由于模型已经做到了动态细分,那么贴图可以选择低质量的图片。之前是因为需要在贴图上画出细腻的阴影,所以不能过渡压缩导致模糊。而新模型是使用动态阴影的,所以贴图压缩78.4%,且理论上可以进一步压缩。

    自适应与性能提高

    动态细分能做到在管线光栅化之前就增加三角面,但这意味每一次渲染都会使用Geometry shader。
    而对比发现GPU的使用率提高了9%左右,GPU的性能虽然可以做到无压力,但进一步的优化也不是无意义的。
    由于透视造成的近大远小,那么离我们较远时,细分可以尽量低级,在很近是,可以尽量高级。依据此思想,我加入了自适应的细分能力来降低性能损耗。
    同时我考虑在模型不使用时,将细分后的模型保存下来,第二次使用时就不用在渲染管线中每一次都细分,但这个需要进一步测试,因为细分后的模型数据量大大增加,对内存和CPU又会有挑战,如何将CPU与GPU做一个均衡优化也一直是个难题,这里后期跟进。

    Shader着色优化

    fresnel效果

    fresnel效果能够产生更合理的反射效果,在渲染时会将不同材质的折射考虑进去。强烈的反射会让材质显得更通透。
    数学公式不复述了,即代码实现了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模型优化后达到了工业级使用的级别,但渲染方案依然有优化的空间,而跨平台的实现方案亦在观察之列。

    图形学博大精深,我时常感觉自己像个小学生对面对的问题不知所措,时常对相关知识阅读三遍也不能理解。现在我也只是了解了其中冰山一角,工作就像求学,得保持对未知的好奇和渴望,同时要保有敬畏之心。

    相关文章

    Animoji实现方案分享

    相关文章

      网友评论

        本文标题:Animoji模型优化方案总结

        本文链接:https://www.haomeiwen.com/subject/auxdactx.html