在OpenGL中使用各异向性纹理过滤
1. 基本的纹理过滤
纹理是数据的简单矩阵排列——例如,颜色数据、亮度数据或者颜色和alpha(透明度)数据。纹理数组中的每一个独立的数值通常称为一个纹理单元。纹理映射是一种将纹理图像应用于物体表面的技术(就是把图像贴到构成物体表面的多边形上去),就像该图像是一种贴画纸或玻璃纸附着于物体的表面上。
那么什么是纹理过滤呢?当三维空间里面的多边形经过坐标变换、投影、光栅化等过程,变成二维屏幕上的一组象素的时候,对每个象素需要到相应纹理图像中进行采样,这个过程就称为纹理过滤。
纹理过滤通常分为2种情况:
a) 纹理被缩小 比如说一个8 x 8的纹理贴到一个平行于xy平面的正方形上,最后该正方形在屏幕上只占4 x 4的象素矩阵,这种情况下一个象素对应着多个纹理单元。
b) 纹理被放大 这种情况刚好跟上面相反,假如我们放大该正方形,最后正方形在屏幕上占了一个16 x 16的象素矩阵,这样就变成一个纹理单元对应着多个象素。
通常的纹理过滤的方法有2种:线性过滤和三线性过滤。也可以设置不进行任何过滤操作。(OpenGL允许为上面两种情况分别设置不同的过滤方法)
a) 不进行任何过滤操作的速度最快也最简单,只是针对每一个象素对最接近它的纹理单元进行采样,可用于上面两种情况。但是这种纹理过滤方法的效果最差,在屏幕显示的图像会显得十分模糊。
b) 线性过滤也比较简单,每个象素要对最接近它的2 x 2的纹理单元矩阵进行采样,取4个纹理单元的平均值,也可用于上面的两种情况。这种纹理过滤方法的效果比上面的要好很多。
c) 三线性过滤相对的比较复杂,它只能用于纹理被缩小的情况,需要先构造纹理图像的mipmap,mip的意思是“在狭窄的地方里的许多东西”,mipmap就是对最初的纹理图像构造的一系列分辨率减少并且预先过滤的纹理图。对于一个8 x 8的纹理来说需要为它构造4 x 4、2 x 2、1 x 1这三个mipmap。如果正方形被缩小到在屏幕上占6 x 6的象素矩阵,一个象素的采样过程就变成这样,首先是到8 x 8的纹理图中进行对最接近它2 x 2的纹理单元矩阵进行采样(也就是上面的线性过滤);其次是到4 x 4的纹理图中重复上面的过程;接着把上面两次采样的结果进行加权平均,得到最后的采样数据。可以看出整个过程一共进行了三次的线性过滤,所以这种方法叫做三线性过滤,它的效果是三种纹理过滤方法里面最好的。
2. 各异向性纹理过滤
各异向性纹理过滤不是单独使用而是和前面所述的其他过滤方法结合一起使用的。
假设Px为纹理在x坐标方向上的缩放的比例因子;Py为纹理在y坐标方向上的缩放的比例因子;Pmax为Px和Py中的最大值;Pmin为Px和Py中的最小值。当Pmax/Pmin等于1时,也就是说Px等于Py,纹理的缩放是各同向的;但是如果Pmax/Pmin不等于1而是大于1,Px不等于Py,也就是说纹理在x坐标方向和在y坐标方向缩放的比例不一样,纹理的缩放是各异向的,Pmax/Pmin代表了各异向的程度。
举个例子来说,64 x 64的纹理贴到一个开始平行于xy平面的正方形上,但是正方形绕y轴旋转60度,最后投影到屏幕上占了16 x 32的象素矩阵。纹理在x坐标方向上缩放的比例因子为64/16等于4,在y坐标方向缩放的比例因子为64/32等于2,Pmax等于4,Pmin等于2。缩放的各异向程度为2。当把各异向性过滤和线性过滤结合起来的时候,应该是对最接近象素的4 x 2的纹理单元矩阵采样才合理,因为一个象素在x坐标方向上对应了更多的纹理单元(Px > Py)。即使是纹理在一个轴方向上缩小而在另一个轴方向上放大,处理的过程也是一样的(注意的是如果纹理在一个轴方向上缩小而在另一个轴方向上放大,OpenGL仍然把它当作是纹理被缩小的情况,将采用为纹理缩小情况设置的过滤方法为基本过滤方法,然后再加上各异向性过滤)。假设被贴图的正方形最后在屏幕上占了一个128 x 32 的象素矩阵,纹理在x坐标方向上缩放的比例因子为64/128等于0.5,在y坐标方向缩放的比例因子为64/32等于2,由于Py > Px 且 Pmax/Pmin等于4,所以当把各异向性过滤和线性过滤结合起来的时候,应该对最接近象素的2 x 8的纹理单元矩阵进行采样。三线性过滤和各异向性过滤结合的过滤方法的步骤跟前面单独的三线性过滤方法大致是一样的,只是前面两步采用了各异向性过滤和线性过滤结合的方法。
通常情况下采取线性过滤或者三线性过滤就可以得不错的效果,但是在某些特殊的情况下,特别是把一个都是线状条纹的纹理图贴到一个绕x或者是y轴旋转角度很大的多边形上的时候,比如将人的头发纹理贴到构成人的头顶的多边形,即使是三线性过滤的效果也不能令人满意,只有将各异向过滤方法和三线性过滤或者线性过滤的方法结合起来才能得到完美的效果。
3. 怎样在OpenGL中使用各异向性纹理过滤
在OpenGL里面使用各异向性纹理过滤首先要系统运行的OpenGL实现支持EXT_texture_filter_anisotropic 这个OpenGL扩展。
OpenGL里面的各异向性纹理过滤的参数设置是独立于纹理缩小和放大这两种情况的,也就是说不需要为这两种情况进行分别设置。参数设置十分简单,只有一个参数就是最大各异向程度(TEXTURE_MAX_ANISOTROPY_EXT)。因为纹理缩放的各异向程度越大,就需要对更多的纹理单元进行采样,这样在处理速度上是不可接受的,所以必须设置一个最大各异向程度,当OpenGL进行各异向性过滤的时候,采用的各异向程度参数为纹理缩放的各异向程度和最大各异向程度之间的最小值,也就是说当纹理缩放的各异向程度大于设置的最大各异向程度时,将使用设置的最大各异向程度作为过滤使用的参数。显然可见,当该参数设置为1的时候就是不进行各异向性过滤,1也是OpenGL为这个参数设定的缺省设置。另外还可以通过查询MAX_TEXTURE_MAX_ANISOTROPY_EXT获得该OpenGL实现支持的最大各异向程度。
下面是一段示例程序:
//假设这是一个二维纹理并且已经设置了mipmap
//获得运行的OpenGL实现支持的最大各异向程度
Glfloat largest_supported_anisotropy;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,
&largest_supported_anisotropy);
//设置纹理缩小时采用的过滤方法,这里设置的是三线性过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
//设置纹理放大时采用的过滤方法,这里设置的是线性过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
//用OpenGL实现支持的最大各异向程度设置最大各异向程度参数
glTexParameterf(GL_TEXTURE_MAX_ANISOTROPY_EXT,
largest_supported_anisotropy);