在开发一些相对较大的场景时,例如:一片铺满相同草地纹理的丘陵地形,如果不采用一些技术手段,就会出现远处的丘陵较近处的丘陵相比更加的清晰的视觉效果,而这种效果与真实世界中近处的物体清晰远处物体模糊的效果是相违背的。
这是因为采用“透视投影”
进行三维场景的绘制过程中,会产生近大远小的效果,而远处的丘陵与近处丘陵在绘制过程中采用的却是同一幅纹理图。如下图所示为未采用Mipmap纹理贴图和采用Mipmap纹理贴图后的运行效果。
从两幅运行效果图可以看出:
观察了采用生成Mipmap纹理的山体运行效果图后,下面对对Mipmap纹理的生成进行介绍。
生成Mipmap纹理不但要经过,纹理id的生成、纹理id的绑定、纹理过滤、指定纹理图像几个阶段还要有一个生成Mipmap纹理的阶段。
此处重点介绍生成Mipmap纹理过程中的,纹理过滤与生成Mipmap纹理两个阶段。
生成 Mipmap 纹理时绑定纹理、纹理过滤阶段经常使用的纹理加载方法代码举例如下:
// 进行纹理采样 并 生成Mipmap纹理
public int initTexture(Bitmap bitmap)
{
//
// (1)、生成纹理ID
int[] textures = new int[1];
GLES30.glGenTextures
(
1, //产生的纹理id的数量
textures, //纹理id的数组
0 //偏移量
);
//
// (2)、绑定纹理ID
int textureId=textures[0];
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId);
//
// (3)、选择Mipmap纹理采样方式:最近点采样、线性采样、三线性采样等
// 大纹理图绑定到小的三维图元上时:采用三线性采样方式
GLES30.glTexParameteri ( GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
// 小纹理图绑定到大的三维图元上时:选择最邻近的mipmap层,使用线性采样算法进行纹理采样。
GLES30.glTexParameteri ( GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_NEAREST);
//
// (4)、纹理拉伸方式:截取或重复
// S方向采用 重复纹理
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_REPEAT);
// T方向采用 重复纹理
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_REPEAT);
//
// (5)、加载纹理图
GLUtils.texImage2D
(
GLES30.GL_TEXTURE_2D, //纹理类型,在OpenGL ES中必须为GL30.GL_TEXTURE_2D
0, //纹理的层次,0表示基本图像层,可以理解为直接贴图
bitmap, //纹理图像
0 //纹理边框尺寸
);
//
// (6)、生成Mipmap纹理
GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);
//释放纹理图
bitmap.recycle();
//返回纹理ID
return textureId;
}
initTexture为将Bitmap图片转化为一个纹理的全部代码实现,其包含了纹理生成、Mipmap纹理采样的全过程:
生成Mipmap纹理时:
与通常的生成加载一个普通的2D纹理不同,生成Mipmap纹理是由大到小生成一组纹理
。
例如:对于一个8x8像素的纹理来说,
若构建Mipmap纹理,OpenGL会为其构造 4x4、2x2、1x1 这三个纹理
(这三个纹理就是一组纹理)。
在纹理使用阶段:
比如前边山地效果图,OpenGL使用纹理时,会根据
开发者选择的纹理采样算法
,从 Mipmap 纹理组中
,按算法要求选择合适的一个或相邻的两个纹理进行纹理贴图和纹理采样
,从而构建远处模糊、近处清晰的效果
。
这里边儿涉及到的可选择Mipmap纹理采样算法有:
GL_LINEAR_MIPMAP_LINEAR
三线性采样;GL_NEAREST_MIPMAP_NEAREST
:选择最邻近的 mipmap 层,纹理采用最近点采样;GL_NEAREST_MIPMAP_LINEAR
:选择相邻的两个 mipmap 层,分别使用最近点采样后,结果进行进行加权平均;GL_LINEAR_MIPMAP_NEAREST
:选择 最邻近的 mipmap 层,使用线性采样算法进行纹理采样。要介绍以上这几种Mipmap纹理采样算反,我们先要认识两个主要的OpenGL API。
生成 Mipmap 纹理时,涉及到两个主要的OpenGL API函数方法:
glTexParameteri
glGenerateMipmap
对于一个8x8像素的纹理来说,
若构建Mipmap纹理,OpenGL会为其构造 4x4、2x2、1x1 这三个纹理
(这三个纹理就是一组纹理)。这里大家应该能注意到,我特意在 glTexParameter* 后边儿带了一个星,这不是书写错误。glTexParameter 存在多个方法,开发者常用的为:glTexParameteri 与 glTexParameterf
。
glTexParameteri 与 glTexParameterf方法的作用:
正如以上加载代码举例中所示,用来指定纹理的采样方式:最近点采样、线性采样、Mipmap纹理采样等;指定纹理的拉伸方式:纹理截取、重复等
(1) 其函数原型为:
void glTexParameteri (GLenum target, GLenum pname, GLint param);
void glTexParameterf (GLenum target, GLenum pname, GLfloat param);
(2) 方法参数
target:为处于激活状态的纹理单元指定纹理类型,参数为GL_TEXTURE_2D。
pname:指定纹理参数,可以为GL_TEXTURE_MIN_FILTER、 GL_TEXTURE_MAG_FILTER、GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T
param:param的值根据pname参数的不同而取不同的值,具体见下表所示:
有些朋友会问 glTexParameteri与glTexParameterf有什么区别?
其实两个函数方法:功能完全相同,只是最后一个输入参数存在差异
。
glTexParameteri
方法;GL_TEXTURE_MIN_LOD
或GL_TEXTURE_MAX_LOD
时,需选择glTexParameterf
方法。我们观察两个方法的原型函数:
可以看到其只是最后一个参数的类型不同,其他并无区别。
void glTexParameteri (GLenum target, GLenum pname, GLint param);
void glTexParameterf (GLenum target, GLenum pname, GLfloat param);
关于 GL_TEXTURE_MIN_LOD 与 GL_TEXTURE_MAX_LOD 的官方说明:
前边说道过:
glGenerateMipmap方法在生成Mipmap纹理时,不是生成一个纹理,而是由大到小生成一组纹理。例如:对于一个8x8像素的纹理来说,
若构建Mipmap纹理,OpenGL会为其构造 4x4、2x2、1x1 这三个纹理
(这三个纹理就是一组纹理)。
英文API描述为:
generate a complete set of mipmaps for a texture object。
(1) 其函数原型为:
void glGenerateMipmap (GLenum target);
(2) 方法参数
target
为处于激活状态的纹理单元指定纹理类型。参数为 GL_TEXTURE_2D。正如第一部分代码举例中,我们可以看到glTexParameter* 这个OpenGL API可以帮助开发人员完成Mipmap纹理采样
与 指定纹理的拉伸方式
。
// (3)、选择Mipmap纹理采样方式:最近点采样、线性采样、三线性采样等
// 大纹理图绑定到小的三维图元上时:采用三线性采样方式
GLES30.glTexParameteri ( GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
// 小纹理图绑定到大的三维图元上时:选择最邻近的Mipmap层,使用线性采样算法进行纹理采样。
GLES30.glTexParameteri ( GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_NEAREST);
//
// (4)、纹理拉伸方式:截取或重复
// S方向采用 重复纹理
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_REPEAT);
// T方向采用 重复纹理
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_REPEAT);
对于OpenGL API
void glTexParameteri (GLenum target, GLenum pname, GLint param);
void glTexParameterf (GLenum target, GLenum pname, GLfloat param);
我们知道当 pname 的值为 GL_TEXTURE_MIN_FILTER
或 GL_TEXTURE_MAG_FILTER
时,这个时候指的是指定纹理的采样方式
。:
但在正式介绍纹理采样之前,先要对GL_TEXTURE_MIN_FILTER
或 GL_TEXTURE_MAG_FILTER
这两个枚举进行简单介绍。
这两个枚举类型的含义是:
纹理图被放大
),采用 MAG
采样;纹理图被缩小
),采用 MIN
采样。设置纹理过滤方式 pname | param |
---|---|
GL_TEXTURE_MIN_FILTER或GL_TEXTURE_MAG_FILTER | GL_NEAREST、GL_LINEAR、GL_LINEAR_MIPMAP_LINEAR、GL_LINEAR_MIPMAP_NEAREST、GL_NEAREST_MIPMAP_LINEAR、GL_NEAREST_MIPMAP_NEAREST |
当 pname 的值为 GL_TEXTURE_MIN_FILTER
或 GL_TEXTURE_MAG_FILTER
时,这个时候指的是指定纹理的采样方式
。:
GL_NEAREST
:最近点采样GL_LINEAR
:线性采样GL_LINEAR_MIPMAP_LINEAR
:三线性采样对于一个8x8像素的纹理来说:
若构建Mipmap纹理,需要为其构造4x4、2x2、1x1这三个纹理。如果一个三维空间中的矩形图片在屏幕上占 6x6 像素点,那么纹理采样过程就变成:
因为整个过程一共进行了三次的线性采样,所以这种方法叫做三线性采样。
GL_NEAREST_MIPMAP_NEAREST
:GL_NEAREST_MIPMAP_LINEAR
:GL_LINEAR_MIPMAP_NEAREST
:对于OpenGL API
void glTexParameteri (GLenum target, GLenum pname, GLint param);
void glTexParameterf (GLenum target, GLenum pname, GLfloat param);
当 pname 的值为 GL_TEXTURE_WRAP_S
或 GL_TEXTURE_WRAP_T
时,这个时候指的是指定纹理的拉伸方式
,那么 param 可选的值为:
设置纹理S、T方向拉伸方式 pname | param |
---|---|
GL_TEXTURE_WRAP_S或GL_TEXTURE_WRAP_T | GL_REPEAT、GL_CLAMP_TO_EDGE |
一般我们给定纹理的在S与T方向的纹理坐标时都是在 [0,1]之间,但纹理在S与T方向的坐标值也是可以大于1的。
当给定纹理的在S与T方向的坐标值分别为[0,4]时,在纹理坐标的S与T方向上,若设置纹理重复方式均为 GL_REPEAT 重复纹理
,那么运行效果图如下图所示:
当给定纹理的在S与T方向的坐标值分别为[0,4]时,在纹理坐标的S与T方向上,若设置纹理重复方式均为 GL_CLAMP_TO_EDGE 截取纹理
,那么运行效果图如下图所示:
该案例代码为Android 平台OpenGL ES实现举例,有两个作用:
案例源码下载地址:
https://download.csdn.net/download/aiwusheng/58430870
文章首发于公众号”CODING技术小馆“,如果文章对您有帮助,欢迎关注我的公众号。