纹理过滤
1. 为什么在纹理采样时需要texture filter(纹理过滤)。
我们的纹理是要贴到三维图形表面的,而三维图形上的pixel中心和纹理上的texel中心并不一至(pixel不一定对应texture上的采样中心texel),大小也不一定一至。当纹理大于三维图形表面时,导至一个像素被映射到许多纹理像素上;当维理小于三维图形表面时,许多个像素都映射到同一纹理。
当这些情况发生时,贴图就会变得模糊或发生错位。要解决此类问题,必须通过技术平滑texel和pixel之间的对应。这种技术就是纹理滤波。
不同的过滤模式,计算复杂度不一样,会得到不同的效果。过滤模式由简单到复杂包括:Nearest Point Sampling(最近点采样),Bilinear(双线性过滤)、Trilinear(三线性过滤)、Anisotropic Filtering(各向异性过滤)。
在了解这些之前,有必要了解什么是多级纹理贴图(MipMap)和什么是各向同性,各向异性。
2. 什么是MipMap?
Mipmap由Lance Williams 在1983的一篇文章“Pyramidal parametrics”中提出。Wiki中有很详细的介绍(http://en.wikipedia.org/wiki/Mipmap ) . 比如一张256X256的图,在长和宽方向每次减少一倍,生成:128X128,64X64,32X32,16X16,8X8,4X4,2X2,1X1,八张图,组成MipMap,如下图示。
Mipmap早已被硬件支持,硬件会自动为创建的Texture生成mipmap的各级。在D3D的API:CreateTexture中有一个参数levels,就是用于指定生成mipmap到哪个级别,当不指定时就一直生成到1X1。
3. 什么是各向同性和各向异性?
当需要贴图的三维表面平行于屏幕(viewport),则是各向同性的。当要贴图的三维表面与屏幕有一定角度的倾斜,则是各向异性的。
也可以这样理解,当一个texture贴到三维表面上从Camera看来没有变形,投射到屏幕空间中后U方向和V方向比例仍然是一样的,便可以理解成各向同性。反之则认为是各向异性。
4. Nearest Point Sampling(最近点采样)
这个最简单,每个像素的纹理坐标,并不是刚好对应Texture上的一个采样点texel,怎么办呢?最近点采样取最接近的texel进行采样。
当纹理的大小与贴图的三维图形的大小差不多时,这种方法非常有效和快捷。如果大小不同,纹理就需要进行放大或缩小,这样,结果就会变得矮胖、变形或模糊。
5. Bilinear(双线性过滤)
双线性过滤以pixel对应的纹理坐标为中心,采该纹理坐标周围4个texel的像素,再取平均,以平均值作为采样值。
双线性过滤像素之间的过渡更加平滑,但是它只作用于一个MipMap Level,它选取texel和pixel之间大小最接近的那一层MipMap进行采样。当和pixel大小匹配的texel大小在两层Mipmap level之间时,双线性过滤在有些情况效果就不太好。于是就有了三线性过滤。
6. Trilinear(三线性过滤)
三线性过滤以双线性过滤为基础。会对pixel大小与texel大小最接近的两层Mipmap level分别进行双线性过滤,然后再对两层得到的结果进生线性插值。
三线性过滤在一般情况下效果非常理想了。但是到目前为止,我们均是假设是texture投射到屏幕空间是各向同性的。但是当各向异性的情况时,效果仍然不理想,于是产生了Anisotropic Filtering(各向异性过滤)。
7. Anisotropic Filtering(各向异性过滤)
先看效果,左边的图采用三线性过滤,右边的图采用各向异性过滤。
各向同性的过滤在采样的时候,是对正方形区域里行采样。各向异性过滤把纹理与屏幕空间的角度这个因素考虑时去。简单地说,它会考滤一个pixel(x:y=1:1)对应到纹理空间中在u和v方向上u和v的比例关系,当u:v不是1:1时,将会按比例在各方向上采样不同数量的点来计算最终的结果(这时采样就有可能是长方形区域)。
我们一般指的Anisotropic Filtering(AF)均是基于三线过滤的Anisotropic Filtering,因此当u:v不为1:1时,则Anisotropic Filtering比Trilinear需要采样更多的点,具体要采多少,取决于是多少X的AF,现在的显卡最多技持到16X AF。
当开启16X AF的时候,硬件并不是对所有的texture采样都用16X AF,而是需要先计算屏幕空间与纹理空间的夹角(量化后便是上面所说的u:v),只有当夹角大到需要16X时,才会真正使用16X.
如果想了解AF的实现原理,可以查阅此篇Paper: “Implementing an anisotropic texture filter”. 现在AF都是硬件实现,因此只有少数人才清楚AF的具体实现,也可以由Pixel Shader来实现AF,当然性能远不如使用硬件来完成。
8. 各过滤模式性能比较。
下表是各种过滤模式采一个pixel需要sample的次数:
类型采样数
Nearest Point Sampling1
Bilinear4
Trilinear8
Anisotropic Filtering 4X32
Anisotropic Filtering 16X128
一般而言,采样数越多,效果最好,但具体使用时要综合考虑性能。
DirectX11 过滤器
1. 倍增(magnification)现象
纹理贴图元素应该被视为在一个连续图像上的离散颜色采样;不应该被视为矩形区域。那么问题是:当我们指定的纹理坐标(u,v)与任何一个纹理元素点都不对应时会产生什么结果?这一问题会发生在如下情景中:当观察点离场景中的一面墙很近时,墙会被放大,以至于会盖住整个屏幕。如果显示器的分辨率为1024×1024,墙体纹理的分辨率为256×256,那么就会出现倍增(magnification)问题——我们将要用很少的纹理元素来覆盖很多的像素。在本例中,每个纹理元素要覆盖4个像素。当顶点纹理坐标在三角形表面上进行插值时,每个像素都会得到一对唯一的纹理坐标。所以,像素的纹理坐标不会与任何一个纹理元素点对应。 我们可以使用插值方法来估算纹理元素之间的颜色。图形硬件提供了两种插值方法:常量插值和线性插值。线性插值是我们最常用的插值方法。
(1). 1D过滤
下图说明c在1D空间中的插值方法:假设有一个包含256个采样点的1D纹理以及一个插值纹理坐标u = 0.126484375。那该纹理坐标对应的纹理元素为0.126484375×256 = 32.38。当然,这个值位于两个纹理采样点之间,我们必须使用插值来估算它。
(a)给出纹理元素点,我们构造一个分段常量函数来估算纹理元素点之间的值;这种方法有时也称为最近邻接点采样(nearest neighbor point sampling),因为它总是用最近的纹理元素点的值作为采样结果。(b)给出纹理元素点,我们构造一个分段线性函数来估算纹理元素点之间的值。
(2). 2D过滤
2D线性插值也称为双线性插值(bilinear interpolation),如下图所示。给出一对位于4个纹理元素之间的纹理坐标,我们在u方向上进行两次1D线性插值,然后在v方向上进行一次1D线性插值。
(这里有4个纹理元素点cij、ci,j+1,ci+1,j、ci+1,j+1。我们想要估算c点的颜色,它位于这4个纹理元素点之间。在本例中,c位于cij右侧0.75单位、cij下方0.38单位处。我们先在上面的两个颜色之间进行1D线性插值得到cT。然后,在下面的两个颜色之间进行1D线性插值得到cB。最后,在cT和cB之间进行线性插值得到c。)
(3). 常量插值与线性插值的区别
下图说明了常量插值和线性插值之间的区别。可以看到,常量插值会使图像出现明显的块状。而线性插值较为平滑,但是与真实数据(例如,一幅高分辨率的纹理)相比,通过插值得到的衍生数据仍然不够理想。
(我们在一个立方体放大一幅板条箱纹理,使倍增问题出现。左图使用常量插值,可以看到它产生了明显的斑块;这很容易理解,因为插值函数是离散的(第一幅图a),它所形成的颜色过渡显得生硬而不平滑。右图使用线性插值,由于插值函数是连续的,所以它产生的图像较为平滑。)
需要强调的是,由于在交互式3D程序中观察点的位置可以自由移动,所以我们对于倍增问题没有一种非常彻底的解决办法。在一定距离内,纹理可以有较好的效果,但是当观察点与物体之间的距离越来越近时,效果会急转直下。使用更高分辨率的纹理可以缓解一问题。
注意:在纹理映射的环境下,使用常量插值来求解纹理元素之间的颜色值的过程也称为点过滤(point filtering),使用线性插值来求解纹理元素之间的颜色值的过程也称为线性过滤(linear filtering)。点过滤和线性过滤是Direct3D使用的术语。
2. 缩减(minification)现象
缩减(minification)与倍增的情况恰好相反。在缩减中,较多的纹理元素会被映射为较少的像素。例如,考虑下面的情景:我们将一幅256×256的纹理映射到墙体上。然后把观察点对准墙体,并向后移动观察点,使墙体逐渐变小,直到墙体在屏幕上只占64×64的像素区域为止。现在,我们要把256×256个纹理元素映射为64×64个屏幕像素。在这一情景中,像素的纹理坐标不会与纹理贴图中的任何一个纹理元素对应。常量和线性插值过滤器仍然适用于缩减情况。不过,缩减的处理工作稍多一些。简单的讲,我们要把256×256个纹理元素均匀地缩减为64×64个纹理元素。多级渐近贴图映射(mipmapping)为这一工作提供了一种高效的估值手段,只是要额外占用一些内存。在初始化时(或创建资源时),通过对图像进行降阶采样生成纹理的多个缩略版本来创建多级渐近纹理链(mipmap chain,参见下图)。求平均值的工作是根据多级渐近贴图的大小提前计算出来的。在运行时,图形硬件会根据程序员指定的多级渐近贴图参数执行两种不同的操作:
1.为纹理映射挑选一个与屏幕几何体分辨率最匹配的多级渐近纹理层,根据需要在多级渐近纹理层上使用常量插值或线性插值。这种用于多级渐近纹理的操作称为点过滤(point filtering),因为它与常量插值很像——它只为纹理映射挑选一个最接近的多级渐近纹理层。
2.为纹理映射挑选两个与屏幕几何体分辨率最匹配的多级渐近纹理层(其中,一个比屏幕几何体分辨率大一些,另一个比屏幕几何体分辨率小一些)。然后,在这两个多级渐近纹理层上使用常量插值或线性插值,分别取出一个纹理颜色。最后,在这两个纹理颜色之间进行插值。这种用于多级渐近纹理的操作称为线性过滤(linear filtering),因为它与线性插值很像——它在两个最匹配的多级渐近纹理层之间进行线性插值。
(多级渐近纹理链;每个后续多级渐近纹理层的尺寸都是前一层的1/2,纹理层的下限尺寸是1×1。)
通过在多级渐近纹理链中选择最佳纹理层,可以使缩减操作的总开销降至最低。
3. 如何创建多级渐近纹理?
多级渐近纹理层即可以由美术师手工创建,也可以由过滤算法生成。
DDS图像格式(DirectDraw Surface格式)可以直接把多级渐近纹理层存储在文件中;在这种情况下,只需要简单的读入数据——不需要在运行执行任何与多级渐近纹理层相关的算法。DirectX纹理工具可以为纹理生成多级渐近纹理链,并导出DDS文件。当一个图像文件不包含完整的多级渐近纹理链时,函数D3DX11CreateShaderResourceViewFromFile或D3DX11CreateTextureFromFile会使用指定的过滤算法创建一个多级渐近纹理链(请参阅 SDK文档中D3DX11_IMAGE_LOAD_INFO,尤其是要注意MipFilter数据成员的描述)。我们可以看到,多级渐近纹理映射基本上是自动的。当资源文件不包含多级渐近纹理链时,D3DX11会为我们自动生成一个纹理链。只要启用了多级渐近纹理映射,硬件就会在运行时自动选择正确的多级渐近纹理层。
注意:有时通用的过滤算法可能会丢失你想要保留的细节。例如,在上面的那幅图中,木板上的文字“Direct3D”在最低等级的渐进纹理中非常模糊。如果这不可接受,美工就需要手动创建/调整渐进层次以保留重要的细节。
4. 什么是各向异性过滤?
我们还可以使用各向异性过滤(anisotropic filter)。当多边形的法线向量与摄像机的观察向量夹角过大时(例如,当多边形垂直于观察窗口时),这种过滤可以有效缓解图像的失真问题。只是它的资源占用量较大。不过,当你需要对失真进行校正时,这些消耗是值得的。下图是对各向异性过滤和线性过滤的一个比较。
板条箱的顶面几乎垂直于观察窗口。(左图)使用线性过滤,板条箱的顶面显得极其模糊。(右图)在同样的角度上,使用各向异性过滤得到的渲染结果要好很多。
文章转载自:https://blog.csdn.net/sinat_24229853/article/details/48880301