大纹理小像素简单说就是纹理很小,但是被平铺在了一块像素超过纹理贴图大小的区域上,这样就会导致一个纹理像素要对应多个屏幕像素,导致纹理失真走样
造成这种情况的原因就是因为采用了最近邻纹理采样的方式,最近邻纹理采样的原理如下
其中,图中的十字表示纹理坐标(纹理坐标是0-1的小数,所以可以取非整数),因为左上角的纹理像素的中心点与纹理坐标的位置最接近,所以,直接将该纹理像素返回,所以最终结果就是一个深蓝色。而再对其他纹理坐标进行采样时,得到的纹理像素也是一个没有过渡的纯色。正式因为没有过渡色,所以这些有差异的纯色组合在一起时,整体的贴图区域就会产生颗粒感。
首先选中离纹理坐标最近的四个纹理像素,然后,做两次横向插值,得到u0和u1,插值公式如下
得到了u0和u1之后,再做一次纵向插值,最终得到红点的像素,插值公式如下
最终得到纹理坐标对应的颜色就是一个周围四个像素混合的结果,能起到一个过渡的作用,所以,经过双线性插值知乎的纹理的颗粒感就没有了,因为插值出了很多的过渡色
双向性插值纹理采样因为引入了过渡色,没有了颗粒感,但是因为贴到了较大的区域,会使得整体的贴图区域变得模糊。
纹理过小并使用最近邻纹理过滤的话,会使得纹理渲染结果变得有颗粒感。但是如果纹理过大的话,也会有问题,如下图所示
产生锯齿的原因:纹理过大,但是每个像素的实际大小并不能覆盖到所有的近处纹理,从而导致采样率较低,于是形成锯齿效果。
产生摩尔纹的原因:由于透视投影会产生近大远小的效果,远处纹理会包含更多的纹理细节,但是,这些丰富的细节却只能用一个像素来呈现(最右边蓝色点),采样率更低,显然会使得远处纹理细节会撕裂的更厉害,所以就会产生摩尔纹。
既然是因为采样频率不够,那么最直观的解决办法就是超采样,提高采样频率,超采样虽然能解决锯齿和摩尔纹问题,但计算成本是太高
那么既然一个采样点不能代表纹理细节,那么就可以将纹理区域内的所有像素进行求和求平均来代替当前像素的纹理。但是,远处和近处的纹理区域不一致,该如何求平均呢?
就像下面这幅图,近处白圈和远处白圈在图片中的像素面积都已差不多的,但是明显远处白圈包含更多的纹理。
为了解决快速查询纹理平均像素值的问题,mipmap算法出现了
level 0代表原始texture,随着level提高,每升一级将4个相邻像素点求均值合为一个像素,默认最高级的level是一个像素,也就是原始所有像素求平均值。
因此,level越大,也就表示了越大纹理区域的平均值,那么,接下来要做的就是根据屏幕像素所代表的纹理区域大小来选选择不同level来计算纹理区域的平均值。
那么,接下来的问题就是如何将屏幕像素区域所代表的纹理区域与level进行一一对应或者说如何计算一个屏幕像素所覆盖的纹理区域?
方法如上图所示,首先,找到像素点及其上方与右方的两个像素点,将其映射到纹理坐标上,然后计算两个纹理坐标的距离,将该距离作为覆盖纹理区域的边长。因为从屏幕坐标映射到纹理坐标,所以,在屏幕坐标上相邻的两个点会发生偏移,所以需要计算当屏幕坐标变化一个单位时,纹理坐标u和v变化了多少,所以就要分别计算uv在x和y方向上的偏导,为了简化计算,直接取两个方向偏导的最大值来作文纹理区域的边长。
比如最后计算出的L的值是4,那么也就意味着屏幕坐标相差1时,纹理坐标相差4。也就是说,需要将一个4*4的纹理坐标求和求平均化成1个像素,而这个像素就需要去level 2中去查询,因为level2中就表示每16个像素融合成一个像素。因此,最终的层级就可以表示为L以2为底的对数,也就是上图中坐标的公式
然而这里还有一个问题,因为最终算出的层数有可能是小数,而mipmap只准备了整数层级进行范围查找,如何计算小数层级的纹理像素呢?
比如计算出来的D=1.8,那么就需先分别到D=1和D=2的纹理坐标,并分别进行一次双线性插值得到纹理像素的值,然后再使用这两个纹理像素的值再进行一次插值
使用mipmap算法处理大纹理后,将纹理贴在像素区域的结果如下
可见,图片整体模糊过度,尤其是远处,原因就是在计算覆盖区域的时候直接使用最大值进行近似计算,导致近似计算出来的区域比实际的区域大,从而模糊的区域变大,最终导致整体效果模糊过度
如下图所示,屏幕空间的像素所对应的纹理区域并不一定是正方形,很多都是长条形,所以需要增加额外的查找空间来匹配纹理区域,所以,各向异性滤波就会多出一些长方形的纹理图片
对比mipmap,mipmap的查找空间只包含对角线所表示的纹理图片,各向异性滤波所产生的的纹理区域更多,所以结果就更加准确
各向异性滤波并不能完全解决过模糊的问题,因为将纹理区域近似为长方形有时候也是不准确的,但是效果也不错
参考:
GAMES101-现代计算机图形学入门-闫令琪_哔哩哔哩_bilibili
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出