关于mipmap的一些小记

1. Mipmaps

纹理对象同样要放在场景中显示,当物理远离视点是,纹理对象会被缩放过滤。在这个持续过程中,会出现闪烁现象。为了避免,可以为纹理对象提供一组不同分辨率的图像数据,叫做Mipmap。
你需要提供从最大尺寸到1x1之间所有2的冥次尺寸的图像;glTexImage2D() api从level=0开始设置,0代表最大尺寸。
可以使用API基于最大尺寸图像为纹理产生Mipmaps,glGenerateMipmap(GLenum target);

有时候可以对Mipmaps的level进行限制,比如不希望有尺寸特别小的mipmap,或者不希望纹理在极大和极小level之间进行切换,这些可以通过API glTexParameter*()完成,参数如下:
GL_TEXTURE_BASE_LEVEL:最大尺寸的level限制;
GL_TEXTURE_MAX_LEVEL:最小尺寸的mipmap level限制;通过这两个值可以减少需要提供的mipmap
GL_TEXTURE_MIN_LOD和GL_TEXTURE_MAX_LOD:则影响OpengGL具体渲染时对mimap level的选择

2. Mipmaps 硬件生成

使用OpenGL的硬件加速来生成所需要的纹理。函数调用如下:

glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);

当这个参数被设置为GL_TRUE时,所有调用glTexImage或者glTexSubImage都会自动更新纹理贴图(第0级)和所有更低层的纹理。通过使用硬件加速会比使用glu库中的gluBuildMipmap要快,但这个特性本来只是个扩展,在OpenGL1.4才被纳入OpenGL核心API的。

LOD(多细节层次)偏好

当mipmapping被启用时,OpenGL会根据各个mip层的大小和几何图形在屏幕上的面积来决定哪一个mip层被选择。OpenGL会选择最合适的mip贴图层与屏幕上的纹理表示形式进行匹配。我们可以告诉OpenGL向后(选择更大的mip层)或向前(选择更小的mipmap层)来调整选择的标准。使用更小的mip层可以提高性能,选择更大的mip层可以锐化纹理映射的对象。这个偏好设置示例如下:

glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, –1.5);

上面会使的细节纹理层倾向于使用更高层的细节(更小的level层参数),从而使得纹理的外观更为锐利,代价是纹理处理的开销大一些。

纹理对象

glTexImage, glTexSubImage和gluBuildMipmaps这些函数的调用消耗的时间特别多。这些函数大量的移动内存,有时需要重新调整数据的格式来适应一些内部的表示。在纹理之间切换或者重新加载不同的纹理图片会带来较大的性能开销。

为了减少这些开销,我们可以使用纹理对象。纹理对象允许你一次性加载多个纹理状态(包括纹理图像),然后在它们之间快速切换。纹理状态由当前绑定的纹理对象来维护。纹理的名称由unsigned int类型来标识。使用下面的函数来生成一定数量的纹理对象:

void glGenTextures(GLsizei n, GLuint *textures);

上面的函数调用指定了纹理对象的数量,和存储纹理对象名称的数组。我们可以通过纹理对象名称来操作纹理状态。绑定其中的一个纹理状态的函数调用如下:

void glBindTexture(GLenum target, GLuint texture);

target参数必须是GL_TEXTURE_1D,GL_TEXTURE_2D或者GL_TEXTURE_3D.texture是纹理名称指定要绑定的纹理对象。在此函数之后,纹理图像的加载和纹理参数的设置都只影响当前绑定的纹理对象。最后删除纹理对象的函数如下:

void glDeleteTextures(GLsizei n, GLuint *texture);

参数的意义与glGenTextures相同。不一定需要每次产生纹理对象使用后就删除所有的纹理对象。多次调用glGenTextures的开销较小,但多次调用glDeleteTextures会有导致一些延迟,原因是需要释放大量的能存空间。在不再需要此纹理对象时,要把该纹理对象删除,防止内存泄露。

判断纹理对象名称是否可用可以通过下面的函数调用来判断:

GLboolean glIsTexture(GLuint texture);

返回GL_TRUE代表可用,GL_FALSE代表不可用。

管理多个纹理

一般而言,在程序初始化时加载多个纹理对象,然后在渲染期间不断地切换,在不再使用时删除纹理对象。下面是一个通道tunnel的例子,此例在启动时加载三个纹理对象,然后通过切换来绘制通道的砖墙,天花板和地板。此例中还演示了不同的mipmap模式,通过右键菜单来切换,通过上下箭头键来在通道中移动,通过左右键来旋转通道。

见:https://my.oschina.net/sweetdark/blog/177812



4. 纹理过滤

I.纹理过滤:

当三维空间里面的多边形经过坐标变换、投影、光栅化等过程,变成二维屏幕上的一组象素的时候,对每个象素需要到相应纹理图像中进行采样,这个过程就称为纹理过滤。

II.纹理过滤通常分为2种情况: 
a) 纹理被缩小 GL_TEXTURE_MIN_FILTER 
比如说一个8 x 8的纹理贴到一个平行于xy平面的正方形上,最后该正方形在屏幕上只占4 x 4的象素矩阵,这种情况下一个象素对应着多个纹理单元。
b) 纹理被放大 GL_TEXTURE_MAG_FILTER 
纹理被放大这种情况刚好跟上面相反,假如我们放大该正方形,最后正方形在屏幕上占了一个16 x 16的象素矩阵,这样就变成一个纹理单元对应着多个象素。

III.几种不同的纹理过滤方式: 
1.最近点采样 GL_NEAREST
2.线性纹理过滤(双线性过滤)GL_LINEAR
3.mipmap纹理过滤(三线性过滤) GL_LINEAR_MIPMAP_LINEAR
4.各向异性过滤

a) 最近点采样
最近点采样,不进行任何过滤操作的速度最快也最简单,只是针对每一个象素对最接近它的纹理单元进行采样,可用于上面两种情况。但是这种纹理过滤方法的效果最差,在屏幕显示的图像会显得十分模糊。
 

b)双线性过滤 Bilinear Interpolation 
线性过滤也比较简单,每个象素要对最接近它的2 x 2的纹理单元矩阵进行采样,取4个纹理单元的平均值,也可用于上面的两种情况。这种纹理过滤方法的效果比上面的要好很多。
这是一种较好的材质影像插补的处理方式,会先找出最接近像素的四个图素,然后在它们之间作差补效果,最后产生的结果才会被贴到像素的位置上,这样不会看到“马赛克”现象。这种处理方式较适用于有一定景深的静态影像,不过无法提供最佳品质。其最大问题在于,当三维物体变得非常小时,一种被称为Depth Aliasing artifacts(深度赝样锯齿),也不适用于移动中的物件。
 

c)三线性过滤 Trilinear Interpolation 
三线性过滤相对的比较复杂,它只能用于纹理被缩小的情况,需要先构造纹理图像的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的纹理图中重复上面的过程;接着把上面两次采样的结果进行加权平均,得到最后的采样数据。可以看出整个过程一共进行了三次的线性过滤,所以这种方法叫做三线性过滤,它的效果是三种纹理过滤方法里面最好的。
这是一种更复杂材质影像插补处理方式,会用到相当多的材质影像,而每张的大小恰好会是另一张的四分之一。例如有一张材质影像是512×512个图素,第二张就会是256×256个图素,第三张就会是128×128个图素等等,总之最小的一张是1×1.凭借这些多重解析度的材质影像,当遇到景深极大的场景时(如飞行模拟),就能提供高品质的贴图效果。一个“双线过滤”需要三次混合,而“三线过滤”就得作七次混合处理,所以每个像素就需要多用21/3倍以上的计算时间。还需要两倍大的存储器时钟带宽。但是“三线过滤”可以提供最高的贴图品质,会去除材质的“闪烁”效果。对于需要动态物体或景深很大的场景应用方面而言,只有“三线过滤”才能提供可接受的材质品质。

d)各向异性过滤 Anisotropic Interpolation 
各向异性过滤是最新型的过滤方法,它需要对映射点周围方形8个或更多的像素进行取样,获得平均值后映射到像素点上。对于许多3D加速卡来说,采用8个以上像素取样的各向异性过滤几乎是不可能的,因为它比三线性过滤需要更多的像素填充率。但是对于3D游戏来说,各向异性过滤则是很重要的一个功能,因为它可以使画面更加逼真,自然处理起来也比三线性过滤会更慢。
各异向性纹理过滤不是单独使用而是和前面所述的其他过滤方法结合一起使用的。它在取样时候,会取8个甚至更多的像素来加以处理,所得到的质量最好。
假设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轴旋转角度很大的多边形上的时候,比如将人的头发纹理贴到构成人的头顶的多边形,即使是三线性过滤的效果也不能令人满意,只有将各异向过滤方法和三线性过滤或者线性过滤的方法结合起来才能得到完美的效果。

  //用OpenGL实现支持的最大各异向程度设置最大各异向程度参数
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, xxx);
  // 注意:这里的 xxx 代表 各向异性过滤 的异向程度参数
  // 你可以设置为:4 , 8 , 16 等。这里也可以在 InitGL() 驱动 OpenGL 函数里写上
  int LrgSupAni;
  glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &LrgSupAni);
  那么 xxx 就得小于或等于 LrgSupAni。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值