多级渐远纹理是什么?为什么使用多级渐远纹理?多级渐远纹理是怎么工作的?
为什么引入Mipmap
当屏幕像素在进行纹理采样时,常常会遇到以下两种情况:
- 纹理分辨率太小,多个屏幕像素通常会落在同一个纹素内,比如下图的左侧部分
- 纹理分辨率太大,单个屏幕覆盖多个纹素,比如下图的右侧部分
纹理分辨率太小
当纹理分辨率过小时,通常使用滤波(Filter)的方式来处理采样结果。图形学中有几种常见的滤波模式:
-
Point(Nearest)
对于目标像素,取距离采样点最近的纹素的颜色值作为最终结果。因此图像看起来会有像素风格的效果 -
Bilinear
对于目标像素,它会找到采样点附近的4个纹素,然后对它们进行线性混合插值得到最终结果。因此图像看起来会有模糊的效果
-
Bicubic
对于目标像素,它会取采样点附近的16个纹素,然后对他们进行双三次插值作为最终结果。
三种滤波模式的效果:
纹理分辨率太大
当纹理分辨率过大时,比如,当纹理距相机特别远时,一个屏幕像素就会覆盖很多纹素,所以该屏幕像素的结果应当为覆盖区域颜色的平均值,当然这是最理想的做法。现实情况是一个屏幕像素覆盖的区域通常是一个经过各种拉伸变换的四边形。比如上面第一张图中的右侧部分,所以很难在保证实时的情况下还要做到准确的范围查询(即求覆盖范围内颜色的平均值)。基于此背景,mipmap出现了。
Mipmap是什么
mipmap在维基百科的解释:Mipmap
mipmap中文中叫多级渐远纹理,多级渐远纹理技术将原纹理提前用滤波处理来得到很多更小的图像,形成一个图像金字塔,每一层都是对上一层图像降采样的结果。mipmap表现形式:
在Unity中,在纹理导入面板中,可以通过勾选Generate Mip Maps来开启多级渐远纹理技术。但是显而易见使用mipmap会增加纹理所占的内存大小,大概会多出33%左右。通过以下对比就会发现,这其实是非常划算的:
Mipmap工作原理
mipmap的工作原理和普通的纹理采样相似,只不过多了一步纹理级别的选择,也就是根据纹理与摄像机的距离选择对应层级上的纹理进行采样。可以把mipmap看成一个金字塔结构,如下图所示(D代表细节层级):
所以mipmap的主要目标就是求解D的值,考虑以下情况:
最理想的情况是一个屏幕像素对应一个纹素,当纹理远离摄像机时,一个屏幕像素覆盖的纹素数量就会增加。所以可以先求得当前一个屏幕像素对应纹素的数量,再去求对应层级。
观察上图,我们可以把红色区域中心到邻近采样点的距离L作为当前一个屏幕像素对应纹素的数量。而纹理存在水平和竖直两个方向,我们取其中较大的值,对应的公式为:
我们观察mipmap金字塔发现,每一层的分辨率都是上层除2,所以很容易得出:
至此我们就能到对应的层级进行纹理采样了。但是我们发现并不一定是个整数,这种情况该怎么办呢?这就引出了Trilinear滤波模式了,这种滤波模式会对 (对D取整)邻近的两层纹理进行采样,然后再进行插值,得到最终结果。
在Unity中,有Point、Bilinear、Trilinear三种滤波模式,需要注意的是,如果纹理没有勾选Generate Mip Maps选项,那么Trilinear得到的结果和Bilinear是一样的。
Mipmap缺点
在以上过程中,我们在求的值的时候,我们取的是水平和竖直两个值中的较大的一个作为参考值,所以最终采样区域可以近似成以下效果:
这就造成了mipmap只能进行正方形范围查询的结果。
所以当遇到以下情况时:
结果就会不尽人意,这时候就引出了Anisotropic filtering(各向异性过滤),关于各向异性的解释可以参考维基百科,各向异性表现形式:
各向异性其实就是分别在水平和垂直方向做了mipmap,虽然各项异性过滤能获取更好的效果,但通过上图我们可以看出纹理所占内存大小最多会增加3倍,so,天下没有免费的午餐。在游戏中常常可以见到各向异性后面跟着一个数字,这个数字代表各向异性生成的层级数量,比如x4 可以对应上图前4行和前4列的结果。
Mipmap为什么可以提升性能
GPU在读取纹理数据的时候是从显存中一块一块读取的,目标纹素附近的纹素也会被加载到GPU cache中,所以当使用mipmap时,更低分辨率的mipmap更容易缓存命中,GPU读取次数大幅减少,性能得以改善。
参考资料
- 《GAMES101》
- 《Unity Shader入门精要》