![eda38d1bba2fdbf44394160adeadd8ac.png](https://i-blog.csdnimg.cn/blog_migrate/44d329b566451e95779ef575cfd7d8ee.jpeg)
本文翻译虎书第十一章纹理映射的11.3部分
11.3 抗锯齿纹理查询
纹理映射的第二个基本问题是抗锯齿。渲染纹理映射的图像是一个采样过程:将纹理映射到表面上,然后将表面投影到图像中,将在整个图像平面上产生2D函数,我们以像素为单位对其进行采样。就像我们在第9章中看到的那样,当图像包含细节或锐利边缘时,使用点样本执行此操作会产生锯齿失真-并且由于纹理的整个点都要引入细节,因此它们成为像我们在图11.3中看到的锯齿问题的主要来源。
就像线条或三角形的抗锯齿光栅化,抗锯齿光线跟踪(参见第13.4节)或对图像进行降采样(参见第9.4节)一样,解决方案是使每个像素不是点样本,而是图像面积的平均值,区域类似像素的大小。使用用于抗锯齿光栅化和光线跟踪的相同超级采样方法,如果有足够的样本,则可以在不更改纹理映射机制的情况下获得出色的结果:像素区域内的许多样本将落在纹理图中的不同位置,并平均使用不同纹理查找计算出的阴影结果是一种精确的方法,可以估算像素上图像的平均颜色。但是,使用详细的纹理需要很多样本才能获得良好的结果,这很慢。在表面存在纹理的情况下,高效地计算该面积平均值是纹理抗锯齿的第一个重要的主题。
纹理图像通常由光栅图像定义,因此,与升采样图像一样,也要考虑重采样问题(参见第9.4节)。解决方案同样可以运用在纹理上:使用重采样滤波在纹理像素之间进行插值。
在以下各节中,我们将对每个主题进行扩展
11.3.1 一个像素的覆盖区
使抗锯齿纹理比其他类型的抗锯齿更为复杂的原因在于,渲染图像与纹理之间的关系不断变化。每个像素值都应计算为图像中属于像素的区域的平均颜色,在通常情况下,像素对应单个表面,这相当于对表面上的区域求平均。 如果表面颜色来自纹理,则依次对纹理的相应部分进行取平均,这称为像素的纹理空间覆盖区。 图11.18说明了正方形区域(可能是较低分辨率图像中的像素区域)覆盖区如何映射到地板纹理空间中大小和形状完全不同的区域
![c555918adfecbf3d570aed3d526bf91b.png](https://i-blog.csdnimg.cn/blog_migrate/09d071998afc2b8d12d7c828750e8608.jpeg)
回忆一下使用纹理进行渲染所涉及的三个空间:将3D点映射到图像中的投影π和将3D点映射到纹理空间中的纹理坐标函数
纹理抗锯齿的核心问题是计算像素覆盖区上纹理的平均值。通常,要精确地做到这一点可能是一件相当复杂的工作:对于一个具有复杂表面形状的遥远物体,覆盖区可能在纹理空间中覆盖了大面积的复杂的形状,或者可能是几个不连续的区域。但是在典型情况下,像素落在表面的平滑区域中,该区域映射到纹理中的单个区域。
因为
但是,为了找到一个用于计算抗锯齿查询的高效算法,需要一些近似值。
![8d1a8cb04e7dd8b7bb5add193a7ca92c.png](https://i-blog.csdnimg.cn/blog_migrate/d8ba2e3b6a7297a16a5b928d1008cec0.jpeg)
当一个函数是平滑的,一个线性近似值通常是有用的。对于纹理抗锯齿的情况,这意味着从图片空间到纹理空间的
![5e9d1530b78534ebfc5e3f73427e60c0.png](https://i-blog.csdnimg.cn/blog_migrate/a4d85a7d160b42137c2a569f3664e487.png)
这四个导数描述了纹理坐标
这种近似的几何解释是,它表示图像中以x为中心的单位大小的正方形像素区域将近似映射到纹理空间中以
导数矩阵
现在,我们已经得到了通常被认为是这种形式问题的“正确答案”:在特定图像空间位置处过滤的纹理样本应该是此点纹理坐标导数定义的平行四边形形状轮廓上的纹理贴图的平均值。这已经有了一些假设,即从图像到纹理的映射是平滑的,但是对于获得出色的图像质量来说它是足够准确的。但是,该平行四边形面积平均值已经太昂贵而无法精确计算,因此使用了各种近似值。纹理抗锯齿的方法在逼近此查找时在速度/质量折衷方面有所不同。我们将在以下各节中讨论这些
11.3.2 重采样
当覆盖区小于纹理像素时,我们会将纹理映射到图像中时将其放大。 这种情况类似于对图像进行上采样,并且主要考虑因素是在纹理像素之间进行插值以生成平滑的图像,其中纹理像素网格不明显。 就像在图像上采样中一样,此平滑过程由重建滤波器定义,该重采样滤波器用于计算纹理空间中任意位置的纹理样本。(参见图11.20。)
![bd7e90c8352852240f96b9a640aeacc8.png](https://i-blog.csdnimg.cn/blog_migrate/ea0c0bc4f7d3b23cd15f69bf624bfce7.png)
注意事项与图像重采样几乎相同,但有一个重要区别。在图像重采样中,任务是在规则的网格上计算输出样本,并且在可分离的重采样滤波器的情况下,这种规则性实现了重要的优化。在纹理过滤中,查找模式不规则,必须分别计算样本。这意味着大型高质量的重建滤波器使用起来非常昂贵,因此,通常用于纹理的最高质量滤波器就是双线性插值。
双线性插值纹理样本的计算与计算通过双线性插值上采样的图像中的一个像素相同。首先,我们根据纹理像素坐标(实值)表示纹理空间采样点,然后读取四个相邻纹理像素的值并取其平均值。纹理通常在单位平方上进行参数设置,并且纹理像素的位置与任何图像中的像素相同,在
Color tex_sample_bilinear(Texture t, float u, float v)
{
u_p = u * t.width - 0.5
v_p = v * t.height - 0.5
iu0 = floor(u_p); iu1 = iu0 + 1
iv0 = floor(v_p); iv1 = iv0 + 1
a_u = (iu1 - u_p); b_u = 1 - a_u
a_v = (iv1 - v_p); b_v = 1 - a_v
return a_u * a_v * t[iu0][iv0] + a_u * b_v * t[iu0][iv1] +
b_u * a_v * t[iu1][iv0] + b_u * b_v * t[iu1][iv1]
}
在许多系统中,此操作成为重要的性能瓶颈,主要是因为从纹理数据中获取四个纹素值涉及到内存延迟。纹理的采样点的模式是不规则的,因为从图像到纹理空间的映射是任意的,但通常是连贯的,因为附近的图像点倾向于映射到可能读取相同纹理像素的附近纹理点。因此,高性能系统具有专门用于纹理采样的特殊硬件,该硬件可处理插值并管理最近使用的纹理数据的缓存,以最大程度地减少从存储纹理数据的内存中提取慢速数据的次数。
阅读第9章后,您可能会抱怨线性插值对于某些要求苛刻的应用程序可能不够平滑。但是,始终可以通过使用更好的滤波器将纹理重新采样到更高的分辨率来使其足够好,从而使纹理足够平滑以至于双线性插值效果良好。
11.3.3 多级渐远纹理
仅在放大纹理的情况下做好插值就足够了:像素占位面积比纹理像素间距小。当像素覆盖区覆盖许多纹理像素时,良好的抗锯齿功能需要计算许多纹理像素的平均值以平滑信号,以便可以对其进行安全采样。
一种计算覆盖区上的平均纹理值的非常准确的方法是找到覆盖区中的所有纹理像素并将其相加。但是,当占用空间很大时,这可能会非常昂贵-仅一次查找就可能需要读取数千个纹素。更好的方法是在不同大小和位置的各个区域上预先计算并存储纹理的平均值。
这个想法的一个非常流行的版本被称为“MIP映射”或mipmapping。Mipmap是一系列纹理,这些纹理均包含相同的图像,但分辨率越来越低。原始的全分辨率纹理图像称为mipmap的基本级别或级别0,而级别1则是通过获取该图像并将其在每个维度中降采样2倍而生成的,从而得到具有像素数量的四分之一。大致来说,此图像中的纹素是0级图像中面积为2乘2纹素的正方形区域的平均值。
可以继续进行此过程,以定义所需的任意多个mipmap级别:通过对k级图像进行降采样2倍来计算k - 1级图像。级别k的纹素对应于原始纹理中面积为
11.3.4 使用Mipmap进行基础的纹理滤波
使用mipmap或图像金字塔,与单独访问许多纹理像素相比,可以更有效地完成纹理过滤。当我们需要在大面积上平均的纹理值时,我们只使用来自更高级别的mipmap的值,这些值已经是图像大面积上的平均值。最简单,最快的方法是从mipmap查找单个值,然后选择级别,以使该级别上的纹素覆盖的大小与像素覆盖区的整体大小大致相同。当然,像素覆盖区的形状可能与纹素表示的(始终为正方形)区域的形状完全不同,我们可以预期会产生一些失真。
暂时搁置一个问题,当像素覆盖区具有拉长的形状时该怎么做,假设覆盖区是宽度D的平方,以全分辨率纹理中的像素表示。哪个级别的mipmap适合采样?由于第k级的纹理像素覆盖了宽度为
所以我们让
![d8466f1ebe89936bba6d2e14fd7a182b.png](https://i-blog.csdnimg.cn/blog_migrate/13b027f2c10045ac85d40cf69e2ee3d8.png)
Color mipmap_sample_trilinear(Texture mip[], float u, float v,matrix J)
{
D = max_column_norm(J)
k = log2(D)
k0 = floor(k); k1 = k0 + 1
a = k1 - k; b = 1 - a
c0 = tex_sample_bilinear(mip[k0], u, v)
c1 = tex_sample_bilinear(mip[k1], u, v)
return a * c0 + b * c1
}
基本的mipmapping可以很好地消除锯齿,但是由于它无法处理细长的或各向异性的像素覆盖区,因此在以掠射角度查看表面时效果不佳。在代表观察者站立的表面的大平面上最常见。 可以以非常陡峭的角度观察地板上较远的点,从而产生非常各向异性的覆盖区,该覆盖区在较大的正方形区域内会发生映射。生成的图像将在水平方向上显得模糊。
11.3.5 各向异性滤波
Mipmap可以与多个查询一起使用,以更好地近似伸长的覆盖区。 这个想法是根据覆盖区的最短轴而不是最大轴选择mipmap级别,然后将沿长轴间隔的几个查找平均在一起。(见图11.21。)
![0375e41d44c6b1700b67390a722e59aa.png](https://i-blog.csdnimg.cn/blog_migrate/5322dce91fb46db3374883d2b9b5952b.jpeg)