Mipmap是一个功能强大的纹理技术,它可以提高渲染的性能以及提升场景的视觉质量。它可以用来解决使用一般的纹理贴图会出现的两个常见的问题:
闪烁,当屏幕上被渲染物体的表面与它所应用的纹理图像相比显得非常小时,就会出现闪烁。尤其当相机和物体在移动的时候,这种负面效果更容易被看到。
性能问题。加载了大量的纹理数据之后,还要对其进行过滤处理(缩小),在屏幕上显示的只是一小部分。纹理越大,所造成的性能影响就越大。
Mipmap就可以解决上面那两个问题。当加载纹理的时候,不单单是加载一个纹理,而是加载一系列从大到小的纹理当mipmapped纹理状态中。然后OpenGl会根据给定的几何图像的大小选择最合适的纹理。Mipmap是把纹理按照2的倍数进行缩放,直到图像为1x1的大小,然后把这些图都存储起来,当要使用的就选择一个合适的图像。这会增加一些额外的内存。在正方形的纹理贴图中使用mipmap技术,大概要比原先多出三分之一的内存空间。
mipmap有多少个层级是有glTexImage的第二个参数level决定的。层级从0开始,0,1,2,3这样递增。如果没有使用mipmap技术,只有第0层的纹理会被加载。在默认情况下,为了使用mipmap,所有层级都会被加载。但我们可以通过纹理参数来控制要加载的层级范围,使用glTexParameteri, 第二个参数为GL_TEXTURE_BASE_LEVEL来指定最低层级的level,第二个参数为GL_TEXTURE_MAX_LEVEL指定最高层级的level。例如我只需要加载0到4层级的纹理:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
除此之外,我们还可通过GL_TEXTURE_MIN_LOD和GL_TEXTURE_MAX_LOD来限制纹理的使用范围(最底层和最高层)。
Mipmap过滤Mipmap的纹理过滤模式如下表:
常量 描述GL_NEAREST 在mip基层上使用最邻近过滤
GL_LINEAR 在mip基层上使用线性过滤
GL_NEAREST_MIPMAP_NEAREST 选择最邻近的mip层,并使用最邻近过滤
GL_NEAREST_MIPMAP_LINEAR 在mip层之间使用线性插值和最邻近过滤
GL_LINEAR_MIPMAP_NEAREST 选择最邻近的mip层,使用线性过滤
GL_LINEAR_MIPMAP_LINEAR 在mip层之间使用线性插值和使用线性过滤,又称三线性mipmap
如果纹理过滤选择为GL_NEAREST或GL_LINEAR模式,那么只有基层的纹理会被加载,其他的纹理将会被忽略。我们必须指定其中一个mipmap过滤器,这样才能使用所有已加载的纹理。这个mipmap过滤器的常量是GL_FILTER_MIPMAP_SELECTOR的形式。其中FLILTER指定了过滤模式,SELECTOR指定了如何选择mipmap层。例如GL_NEAREST_MIPMAP_LINEAR模式,它的SELECTOR是GL_LINEAR,它会在两个最邻近的mip层中执行线性插值,然后得出的结果又由被选择的过滤器GL_NEAREST进行过滤。
其中GL_NEAREST_MIPMAP_NEAAREST具有很好的性能,也能够解决闪烁的问题,但在视觉效果上会比较差。其中GL_LINEAR_MIPMAP_NEAREST常用于游戏加速,使用了质量较高的线性过滤,和快速的选择的方式(最邻近方式)。
使用最邻近的方式作为mipmap选择器的效果依然不能令人满意。从某一个角度去看,常常可以看到物体表面从一个mip层到另一个mip层的转变。GL_LINEAR_MIPMAP_LINEAR和GL_NEAREST_MIPMAP_LINEAR过滤器在mip层之间执行一些额外的线性插值,以消除不同层之间的变换痕迹,但也需要一些额外的性能开销。GL_LINEAR_MIPMAP_LINEAR具有最高的精度。
构建Mip层mip贴图需要加载更小的基本纹理图像以便使用。但我们手头上没有这些更小的纹理图像,怎么办呢。GLU函数库提供了一个很方便的方法gluBuildMipmaps,它会帮我们缩放图像并通过类似glTexImage的函数加载图像。支持1维、2维、3维的图像,函数原型如下:
int gluBuild1DMipmaps(GLenum target, GLint internalFormat, GLint width, GLenum format, GLenum type, const void *data);
int gluBuild2DMipmaps(GLenum target, GLint internalFormat, GLint width, GLint height, GLenum format, GLenum type, const void *data);
int gluBuild3DMipmaps(GLenum target, GLint internalFormat, GLint width, GLint height, GLint depth, GLenum format, GLenum type, const void *data);
参数的意义与glTexImage相同。但没有level参数来指定mipmap的层级,也不支持纹理边界。使用这个函数未必能够获得高质量的较小的纹理贴图,只是比较方便。要使用高质量的不同比例的纹理贴图,最好是自己手工制作,然后加载。GLU库是使用box过滤器(简单地就是对给定范围的像素进行加权平均,例如7X7的box filter,你就需要对49个像素进行平均)
新版的GLU库中可以使用gluBuild*MipmapLevels来更好的控制加载的纹理层级
int gluBuild1DMipmapLevels(GLenum target, GLint internalFormat, GLint width, GLenum format, GLenum type, GLint base, GLint max, const void *data);
int gluBuild2DMipmapLevels(GLenum target, GLint internalFormat, GLint width, GLint height, GLenum format, GLenum type, GLint base, GLint max, const void *data);
int gluBuild3DMipmapLevels(GLenum target, GLint internalFormat, GLint widht, GLint height, GLint depth, GLenum format, GLenum type, GLint base, GLint max, const void *data);
创建从base到max层的纹理数据。
Mipmaps 硬件生成使用OpenGL的硬件加速来生成所需要的纹理。函数调用如下:
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);