在开发一些相对较大的场景时,例如:一片铺满相同草地纹理的丘陵地形,如果不采用一些技术手段,就会出现远处的丘陵较近处的丘陵相比更加的清晰的视觉效果,而这种效果与真实世界中近处的物体清晰远处物体模糊的效果是相违背的。

这是因为采用“透视投影”进行三维场景的绘制过程中,会产生近大远小的效果,而远处的丘陵与近处丘陵在绘制过程中采用的却是同一幅纹理图。如下图所示为未采用Mipmap纹理贴图和采用Mipmap纹理贴图后的运行效果。

从两幅运行效果图可以看出:

  • 第一幅图 近处山体与远处山体在视觉效果上清新程度几乎相同,
  • 第二幅图 远处的山体较近处相比较产生了模糊的效果。

观察了采用生成Mipmap纹理的山体运行效果图后,下面对对Mipmap纹理的生成进行介绍。

生成Mipmap纹理不但要经过,纹理id的生成、纹理id的绑定、纹理过滤、指定纹理图像几个阶段还要有一个生成Mipmap纹理的阶段。

此处重点介绍生成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纹理采样的全过程:

  • (1)、生成纹理ID
  • (2)、绑定纹理ID
  • (3)、选择Mipmap纹理采样方式:最近点采样、线性采样、三线性采样等
  • (4)、纹理拉伸方式:截取或重复
  • (5)、加载纹理图
  • (6)、生成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
  • 生成Mipmap纹理的方法:glGenerateMipmap

    其中glGenerateMipmap方法在生成MipMap纹理时,不是生成一个纹理,而是由大到小生成一组纹理。例如:对于一个8x8像素的纹理来说,若构建Mipmap纹理,OpenGL会为其构造 4x4、2x2、1x1 这三个纹理(这三个纹理就是一组纹理)。

二、API介绍

这里大家应该能注意到,我特意在 glTexParameter* 后边儿带了一个星,这不是书写错误。glTexParameter 存在多个方法,开发者常用的为:glTexParameteri 与 glTexParameterf

  • glTexParameter*
  • glTexParameteri与glTexParameterf区别

2.1 glTexParameter*

glTexParameteri 与 glTexParameterf方法的作用:

正如以上加载代码举例中所示,用来指定纹理的采样方式:最近点采样、线性采样、Mipmap纹理采样等;指定纹理的拉伸方式:纹理截取、重复等

  • 采样方式:

    GL_NEAREST(最近点采样)、GL_LINEAR(线性采样)、Mipmap纹理采样等;
  • 纹理S、T方向的拉伸方式:

    GL_REPEAT(纹理重复)、GL_CLAMP_TO_EDGE(纹理截取);

(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参数的不同而取不同的值,具体见下表所示:

2.2 glTexParameteri与glTexParameterf区别

有些朋友会问 glTexParameteri与glTexParameterf有什么区别?

其实两个函数方法:功能完全相同,只是最后一个输入参数存在差异

  • 两个函数方法,大多数情况下我们可直接使用 glTexParameteri 方法;
  • 但当 pname(第二个参数) 输入参数为 GL_TEXTURE_MIN_LODGL_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 的官方说明:

2.3 glGenerateMipmap

前边说道过:

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);
  • 采样方式:

    GL_NEAREST(最近点采样)、GL_LINEAR(线性采样)、Mipmap纹理采样等;
  • 纹理S、T方向的拉伸方式:

    GL_REPEAT(纹理重复)、GL_CLAMP_TO_EDGE(纹理截取);

3.1 纹理采样

对于OpenGL API

void glTexParameteri (GLenum target, GLenum pname, GLint param);
void glTexParameterf (GLenum target, GLenum pname, GLfloat param);

我们知道当 pname 的值为 GL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER 时,这个时候指的是指定纹理的采样方式。:

但在正式介绍纹理采样之前,先要对GL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER 这两个枚举进行简单介绍。

3.1.1 GL_TEXTURE_MIN_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

3.1.2 纹理采样算法

当 pname 的值为 GL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER 时,这个时候指的是指定纹理的采样方式。:

  • GL_NEAREST:最近点采样

    最近点采样,针对三维图元的像素点对最其最接近它的纹理单元进行采样。纹理采样的效率比较高,但是这种纹理采样方法的效果较差,甚至在屏幕显示的图像会出现模糊。
  • GL_LINEAR:线性采样

    线性采样,每个象素要对其最接近的 nxn 的纹理单元进行采样,取加权平均值。线性采样相比于最近点采样,效率较低,但效果较好。
  • GL_LINEAR_MIPMAP_LINEAR:三线性采样

    三线性纹理采样相对比较复杂,经常适用于纹理被缩小的情况。构建Mipmap纹理图时,mip的意思是 “在狭窄的地方里的许多东西”,Mipmap就是对最初的纹理图像构造的一系列分辨率减少并且预先过滤的纹理图。

    对于一个8x8像素的纹理来说:若构建Mipmap纹理,需要为其构造4x4、2x2、1x1这三个纹理。

    如果一个三维空间中的矩形图片在屏幕上占 6x6 像素点,那么纹理采样过程就变成:

    首先是到 8x8 的纹理图中进行线性采样;

    其次是到 4x4 的纹理图中进行线性采样;

    然后把两次采样的结果进行加权平均,得到最后的采样数据。

    因为整个过程一共进行了三次的线性采样,所以这种方法叫做三线性采样。
  • GL_NEAREST_MIPMAP_NEAREST

    选择最邻近的 Mipmap 层,纹理采用最近点采样;
  • GL_NEAREST_MIPMAP_LINEAR

    选择相邻的两个 Mipmap 层,分别使用最近点采样后,结果进行进行加权平均;
  • GL_LINEAR_MIPMAP_NEAREST

    选择 最邻近的 Mipmap 层,使用线性采样算法进行纹理采样。

3.2 纹理拉伸

对于OpenGL API

void glTexParameteri (GLenum target, GLenum pname, GLint param);
void glTexParameterf (GLenum target, GLenum pname, GLfloat param);

当 pname 的值为 GL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_T 时,这个时候指的是指定纹理的拉伸方式,那么 param 可选的值为:

  • GL_REPEAT 重复纹理
  • GL_CLAMP_TO_EDGE 截取纹理
设置纹理S、T方向拉伸方式 pname param
GL_TEXTURE_WRAP_S或GL_TEXTURE_WRAP_T GL_REPEAT、GL_CLAMP_TO_EDGE

3.2.1 GL_REPEAT

一般我们给定纹理的在S与T方向的纹理坐标时都是在 [0,1]之间,但纹理在S与T方向的坐标值也是可以大于1的。

当给定纹理的在S与T方向的坐标值分别为[0,4]时,在纹理坐标的S与T方向上,若设置纹理重复方式均为 GL_REPEAT 重复纹理,那么运行效果图如下图所示:

3.2.2 GL_CLAMP_TO_EDGE

当给定纹理的在S与T方向的坐标值分别为[0,4]时,在纹理坐标的S与T方向上,若设置纹理重复方式均为 GL_CLAMP_TO_EDGE 截取纹理,那么运行效果图如下图所示:

附案例代码

该案例代码为Android 平台OpenGL ES实现举例,有两个作用:

  • 1、在Android平台,使用OpenGL ES通过加载灰度图,构建山地图形渲染效果;
  • 2、在Android平台,使用 OpenGLES 生成与使用Mipmap纹理,构建远处模糊 近处清晰的效果。

案例源码下载地址:

https://download.csdn.net/download/aiwusheng/58430870

= THE END =

文章首发于公众号”CODING技术小馆“,如果文章对您有帮助,欢迎关注我的公众号。

一文详解 纹理采样与Mipmap纹理——构建山地渲染效果的更多相关文章

  1. 一文详解Hexo+Github小白建站

    作者:玩世不恭的Coder时间:2020-03-08说明:本文为原创文章,未经允许不可转载,转载前请联系作者 一文详解Hexo+Github小白建站 前言 GitHub是一个面向开源及私有软件项目的托 ...

  2. 一文详解 Linux 系统常用监控工一文详解 Linux 系统常用监控工具(top,htop,iotop,iftop)具(top,htop,iotop,iftop)

    一文详解 Linux 系统常用监控工具(top,htop,iotop,iftop)     概 述 本文主要记录一下 Linux 系统上一些常用的系统监控工具,非常好用.正所谓磨刀不误砍柴工,花点时间 ...

  3. 详解Dialog(二)——有关列表的构建

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 前言:这段时间真是忙啊忙啊忙,元旦三天假加了两天班,已经连续六周只放一天了,天天加班到十点多,真是有一口血吐在屏幕上的感觉了,博 ...

  4. 一文详解 OpenGL ES 纹理颜色混合

    在OpenGL中绘制的时候,有时候想使新画的颜色和已经有的颜色按照一定的方式进行混合.例如:想使物体拥有半透明的效果,或者绘制叠加光亮的效果,这时候就需要用到OpenGLES混合. 如上图所示,为石头 ...

  5. 一文详解 OpenGL ES 3.x 渲染管线

    OpenGL ES 构建的三维空间,其中的三维实体由许多的三角形拼接构成.如下图左侧所示的三维实体圆锥,其由许多三角形按照一定规律拼接构成.而组成圆锥的每一个三角形,其任意一个顶点由三维空间中 x.y ...

  6. 一文详解 WebSocket 网络协议

    WebSocket 协议运行在TCP协议之上,与Http协议同属于应用层网络数据传输协议.WebSocket相比于Http协议最大的特点是:允许服务端主动向客户端推送数据(从而解决Http 1.1协议 ...

  7. 1.3w字,一文详解死锁!

    死锁(Dead Lock)指的是两个或两个以上的运算单元(进程.线程或协程),都在等待对方停止执行,以取得系统资源,但是没有一方提前退出,就称为死锁. 1.死锁演示 死锁的形成分为两个方面,一个是使用 ...

  8. 一文详解Redis键过期策略

    摘要:Redis采用的过期策略:惰性删除+定期删除. 本文分享自华为云社区<Redis键过期策略详解>,作者:JavaEdge. 1 设置带过期时间的 key # 时间复杂度:O(1),最 ...

  9. 详解Python Streamlit框架,用于构建精美数据可视化web app,练习做个垃圾分类app

    今天详解一个 Python 库 Streamlit,它可以为机器学习和数据分析构建 web app.它的优势是入门容易.纯 Python 编码.开发效率高.UI精美. 上图是用 Streamlit 构 ...

随机推荐

  1. 提升开发效率的notepad++一些快捷方法(实体类的创建和查询sql语句的编写)

    新手要创建数据库表中,对应字段名的实体类,是不是感觉很麻烦,可以用notepad++快速的把实体类中的字段名进行排版,随后直接粘入idea使用 下面是navicat的演示 选择一个表,右键选择设计表 ...

  2. python实现调用摄像头或打开视频文件

    目录: (一)调用摄像头或打开视频文件代码实现 (二)说明和补充 (一)调用摄像头或打开视频文件代码实现 1 # -*- coding=GBK -*- 2 import cv2 as cv 3 4 5 ...

  3. [atARC058F]Lroha Loves Strings

    贪心,求出前$i$个字符串所能组成的字典序最小的字符串$ans$(特别的,这里的字典序有$ab>abc$),同时保证剩下的长度能通过$l_{i+1},...,l_{n}$拼接 考虑插入一个字符串 ...

  4. [atARC085F]NRE

    令$(S_{a},S_{b})$表示$a_{i}\in S_{a}$且$b_{i}\in S_{b}$的i个数,那么答案相当于$S(0,1)+S(1,0)=S(0,1)+S(\{0,1\},0)-S( ...

  5. [loj2473]秘密袭击

    容易发现答案即$\sum_{S}\sum_{u=1}^{W}[u\le val(S)]=\sum_{u=1}^{W}\sum_{S}[u\le val(S)]$,那么可以枚举权值$u$,并将点权$va ...

  6. Java8-JVM内存区域划分白话解读

    前言 java作为一款能够自动管理内存的语言,与传统的c/c++语言相比有着自己独特的优势.虽然我们无需去管理内存,但为了防范可能发生的异常,我们需要对java内部数据如何存储有一定了解,已应对突发问 ...

  7. 探索JavaScript执行机制

    前言 不论是工作还是面试,我们可能都经常会碰到需要知道代码的执行顺序的场景,所以打算花点时间彻底搞懂JavaScript的执行机制. 如果这篇文章有帮助到你,️关注+点赞️鼓励一下作者,文章公众号首发 ...

  8. Atcoder Grand Contest 020 E - Encoding Subsets(记忆化搜索+复杂度分析)

    Atcoder 题面传送门 & 洛谷题面传送门 首先先考虑如果没有什么子集的限制怎样计算方案数.明显就是一个区间 \(dp\),这个恰好一年前就做过类似的题目了.我们设 \(f_{l,r}\) ...

  9. Codeforces 1491G - Switch and Flip(构造题)

    Codeforces 题目传送门 & 洛谷题目传送门 obviously,难度高一点的构造题对我来说都是不可做题 首先考虑将排列拆成一个个置换环,也就是 \(\forall i\) 连边 \( ...

  10. Break up CF700C

    Break up CF700C 首先考虑只能删一条边的做法,我们可以找出所有的桥,然后随便跑一条 S 到 T 路径,如果这条路径上有桥就说明可以,否则不行 发现这个做法其实是 O(M) 的 那么可以先 ...