浅谈Unity中的Mipmap与Texture Streaming技术
1.MipmapUnity中的Mipmap相当于Texture的Lod技术,为了防止物体离相机较远时因为贴图的采样率不足导致出现条纹。Unity中生成Lod的方法很简单,在Texture的面板中勾选Generate Mip Maps,然后点Apply即可。生成完毕后可在下方查看每一级level所对应的图片,原理应该是均值采样法。内存方面可以看到原来1M的图片变成了1.3M,为什么多出来30%的空间可以看看Mipmap的金字塔排布图。
第5级Mipmap,已经很模糊了
不方便的是Unity中的Mipmap是自动生成的,我们无法手动设置生成多少level的图片,在相机距离texture多少时使用哪个level的图片(Editor里无法设置,但Shader里可设置)。接下来验证Unity的Mipmap Level的算法,参考官方文档,整个工程也是基于Mipmap Visualization 插件进行测试的。关键代码如下,使用纯色图替换texture中各个level的mipmap,这样更容易看出区别。
得到的效果图如下:
可以看到shader中设置mipmap level的时机应该是在片元着色器之前,因为同一个三角形面片中片元的level是一样的(这么做的原因是节省性能?)。同时level的切换自带渐变效果,是经过了插值的,和Unity官方文档中描述的相同。
接下来参考文章在shader中计算贴图mipmap级别,手动计算Mipmap Level。原理是计算相邻像素坐标点对应的uv纹素坐标的差值,代表模型在相机下占据大小的相对值。得到的效果图如下:
可以看出mipmap level的计算大致是正确的(看地面),只是需要加上一个偏移量。另外由于手动计算是在片元着色器中完成的,因此和Unity自带方法不同,每一个三角形面片中片元的level是不同的,即同一个三角面中的像素颜色是不同的。并且由于没有使用插值,看上去很突兀,缺少渐变。
2.Texture Streaming
Texture Steaming技术是解决Shader渲染时整个Texture Mipmap金字塔全部加载的问题,在模型离相机较远时的优化效果很好,换句话说大世界游戏的优化效果很好?参考官方文档。开启方法是在Texture和Project Setting中分别进行设置,设置完之后Unity在相机未看到Mesh时会加载最小的mipmap(可在ProjectSetting->Quality-> MaxLevelReduction设置)。当相机看到后会计算得到一个合理的mipmap level,然后将其他level的mipmap全部裁剪,不进行加载,同样在MaxLevelReduction进行设置。在游戏运行时会得到一个先模糊再清晰的过程。经过测试,在相机距离较远时,可以获得很好的优化效果(1.3MB->85KB),在MaxLevelReduction默认为2的情况下。注意写Editor Demo时要用AssetBundle或Adressable加载Material和Texture,不要直接把Texture放在Asset文件夹下,这样通过Memory Profiler看不出优化,因为Texture在内存中常驻(PS:PC上测试时发现当相机移动时,loaded mipmap level不会变化,参考UWA文章)。
未开启Texture Streaming,图片为1.3M
开启Texture Streaming,图片为85K,和设置有关
改进思路:在cpu处计算Mipmap Level。目的是自由控制加载,Unity提供方法:Texture2D.requestedMipmapLevel。原理应该是在cpu进行模型网格的遍历,得到该模型需要使用的Mipmap Level值,可能要遍历所有三角面片数据。难点在于:
[*]算法实现复杂,要考虑性能消耗,本身就是在用cpu换取存储。每当相机/模型移动后进行调用
[*]资源管理复杂,需要考虑所有资源的加载卸载,以及多个Renderer引用同一个Texture的情况
最终在项目中应用该技术,在安卓端统计Texture的内存,发现只优化了10%。总结原因如下:一个是有很多Texture无法设置为Streaming,例如UI的和草地等需要开Read/Write的,同时Memory Budget和MaxLevelReduction两个参数的设置也很重要。另一个是游戏是卡牌战斗游戏,场景相机普遍离物体/角色很近,并且固定不动,很少像大世界游戏那样看的很远的情况 这部分可以对照UE的实现
页:
[1]