一、纹理概念

纹理可以简单理解为物体表面的图案,OpenGL ES 3.0 中纹理有:2D纹理、2D纹理数组、3D纹理和立方图纹理。一个纹理的单独数据元素称为“纹素”(texture pixel 纹理像素的缩写)。

1. 纹理的坐标系

2. 2D纹理

2D纹理是最基本和常用的纹理,可以把2D纹理想象为一个图像数据的二维数组。2D纹理的纹理坐标用一对2D坐标 (s, t) 指定,有时也称作 (u, v) 坐标。

纹理图像坐标的左下角为 (0.0, 0.0),右上角为 (1.0, 1.0)。在 [0.0, 1.0] 区间之外的坐标也是允许的,在区间外的纹理读取行为由纹理包装模式定义。

3. 2D纹理数组

2D纹理数组常用于存储2D图像的一个动画,数组的每个切片表示纹理动画的一帧。2D纹理数组使用与3D纹理一样的纹理坐标 (s, t, r),其中 r 坐标选择 2D 纹理数组中要使用的切片,(s, t) 坐标选择的方法和 2D 纹理完全一致。

二、纹理对象和纹理加载

1. 创建纹理

首先我们需要创建一个纹理对象,纹理对象就是一个容器,包含渲染所需的纹理数据,例如图像数据、过渡模式、包装模式等。

在 OpenGL ES 中,纹理对象使用一个无符号的整数表示,该整数即纹理对象的一个句柄/ID。

/*** @param n 指定要生成的纹理对象数量* @param textures 一个保存n个纹理对象ID的无符号整数数组*/
void glGenTextures(GLsizei n, GLuint *textures);

2. 删除纹理

纹理对象在不再需要时必须删除,这一步骤通常在应用程序关闭或者游戏级别改变时完成。

/*** @param n 要删除的纹理对象数量* @param textures 一个保存n个纹理对象ID的无符号整数数组*/
void glDeleteTextures(GLsizei n, const GLuint *textures);

3. 绑定纹理

当我们生成并得到纹理对象ID后,如果想对其进行操作,就必须先绑定这个纹理对象,这样后续的操作影响的僵尸我们绑定的这个纹理对象。

/*** @param target 目标纹理,即 GL_TEXTURE_2D、GL_TEXTURE_3D、GL_TEXTURE_2D_ARRAY 或 GL_TEXTURE_CUBE_MAP* @param texture 要绑定的纹理对象ID*/
void glBindTexture(GLenum target, GLuint texture);

4. 解绑纹理

当我们不想再操作这个纹理对象时,可以解绑这个纹理。绑定 0 就是解绑了,同样是使用 glBindTexture 函数,参数传入固定的值 0 即可。

5. 加载图像数据

用于加载2D和立方图纹理的基本函数是 glTexImage2D。此外在 3.0 中还有多种替代方法指定 2D 纹理,如不可变纹理 glTexStorage2DglTexSubImage2D 的结合,将在后续介绍。

/*** @param target 目标纹理,即 GL_TEXTURE_2D、GL_TEXTURE_3D、GL_TEXTURE_2D_ARRAY 或 GL_TEXTURE_CUBE_MAP* @param level 指定要加载的mip级别。第一个级别为0,后续的mip贴图级别递增* @param internalformat 纹理存储的内部格式* @param width 图像的像素宽度* @param height 图像的像素高度* @param border 在OpenGL ES中忽略,保留是为了兼容桌面的OpenGL,传入0* @param format 输入的纹理数据格式* @param type 输入像素数据的类型* @param pixels 包含图像的实际像素数据。像素行必须对齐到用glPixelStroei设置的GL_UNPACK_ALIGNMENT*/
void glTexImage2D(GLenum target, GLint level, GLint internalformat,GLsizei width, GLsizei height, GLint border,GLenum format, GLenum type, const GLvoid *pixels)

关于纹理格式的介绍,可以参考:
https://blog.csdn.net/afei__/article/details/96158388

6. 相关设置

纹理的相关设置大都是通过 glTexParameter[i|f][v] 接口指定。这里介绍两个最常见的设置。

纹理过滤模式

纹理过滤模式,简单的说就是当纹理需要放大和缩小时,应该怎么处理。有关纹理过滤模式的介绍,可以参考:https://blog.csdn.net/afei__/article/details/96484772

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 缩小的情况
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 放大的情况

纹理坐标包装

前面介绍过,纹理坐标系是 [0, 0, 1, 1],那么当坐标超过这个范围时,应该发生怎样的行为。

纹理坐标包装有三种不同的模式:

  • GL_REPEAT : 重复纹理
  • GL_CLAMP_TO_EDGE : 限定读取纹理的边缘
  • GL_MIRRORED_REPEAT : 重复纹理并镜像
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // s轴
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // t轴

7. 示例

创建一个RGB图像纹理为例:

// 纹理对象的句柄
GLuint textureId;
// 一个2*2的图像,四个像素依次为红、绿、蓝、黄,每个像素大小为3个字节
GLubyte pixels[4 * 3] = {255, 0, 0, // Red0, 255, 0, // Green0, 0, 255, // Blue255, 255, 0 // Yellow
};
// 创建一个纹理对象
glGenTextures(1, &textureId);
// 绑定纹理对象
glBindTexture(GL_TEXTURE_2D, textureId);
// 加载纹理
glTexImage2D(GL_TEXTURE_2D, // target0, // levelGL_RGB, // internalformat2, // width2, // height0, // borderGL_RGB, // formatGL_UNSIGNED_BYTE, // typepixels); // pixels
// 设置纹理过滤模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 设置纹理坐标包装
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

上述数据由无符号字节 RGB 三元组组成,范围为 [0, 255]

三、使用纹理

上面我们已经了解到如何创建和加载一个纹理,并得到这样一个纹理id。那么如何在代码中使用这个纹理呢?通过一个实例来一步步了解吧。

1. 着色器脚本

顶点着色器

很简单的一个顶点着色器,仅仅把输入的数据再传给片段着色器即可。

#version 300 es
layout(location = 0) in vec4 vPosition; // 输入的顶点坐标
layout(location = 1) in vec2 vTexCoor; // 输入的纹理坐标
out vec2 v_texCoor; // 输出到片段着色器的纹理坐标
void main() {gl_Position = vPosition;v_texCoor = vTexCoor;
}

片段着色器

使用纹理的真正地方,片段着色器中,使用到的内建函数 texture 作用是从纹理中指定位置读取一个颜色的 vec4。

#version 300 es
precision mediump float; // 指定精度
uniform sampler2D s_texture; // 采样器对象,当前绑定和激活的纹理
in vec2 v_texCoor; // 顶点着色器中输出的纹理坐标
out vec4 fragColor; // 该片段的颜色
void main() {fragColor = texture ( s_texture, v_texCoor ); // 在对应纹理坐标处进行采样并得到对应的颜色
}

2. 创建一个纹理对象

就使用上面的那个示例即可

GLuint loadTexture() {GLuint textureId;// 2*2 RGB data for testGLfloat pixels[4 * 3] = {1.0, 0.0, 0.0, // Red0.0, 1.0, 0.0, // Green0.0, 0.0, 1.0, // Blue1.0, 1.0, 0.0 // Yellow};// 创建一个纹理对象glGenTextures(1, &textureId);// 绑定纹理glBindTexture(GL_TEXTURE_2D, textureId);// 加载数据glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_FLOAT, pixels);// 相关设置glGenerateMipmap(GL_TEXTURE_2D);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// 解除绑定glBindTexture(GL_TEXTURE_2D, 0);// 返回纹理idreturn textureId;
}

3. 使用这个纹理对象

我们以绘制一个矩形为例,即绘制两个三角形。

// 1. 创建一个程序(这里省略了详细代码,可在最后的工程地址中找到)
GLuint g_program = CreateProgram(vertexShaderSource, fragmentShaderSource);
glUseProgram(g_program);// 2. 初始化我们的顶点数据,即传给顶点着色器的数据
// OpenGL的世界坐标系是 [-1, -1, 1, 1],纹理的坐标系为 [0, 0, 1, 1]
// 又由于本例运行在安卓设备上,在安卓中 y 轴是向下的,想用正确的方向观看图像的话,也要注意这一点
GLfloat vertices[] = {// 前三个数字为顶点坐标(x, y, z),后两个数字为纹理坐标(s, t)// 第一个三角形1.0,  1.0,  0.0,     1.0, 0.0,1.0,  -1.0, 0.0,     1.0, 1.0,-1.0, -1.0, 0.0,     0.0, 1.0,// 第二个三角形1.0,  1.0,  0.0,     1.0, 0.0,-1.0, -1.0, 0.0,     0.0, 1.0,-1.0, 1.0,  0.0,     0.0, 0.0
};// 3. 加载纹理
GLuint textureId = loadTexture();
glActiveTexture(GL_TEXTURE0); // 激活TEXTURE0
glBindTexture(GL_TEXTURE_2D, textureId);
// 找到片段着色器中s_texture的位置,并给它赋值
GLint location = glGetUniformLocation(g_program, "s_texture");
glUniform1i(location, 0); // 因为激活的是TEXTURE0,所以要给这个纹理赋值0// 4. 加载顶点数据
// 给vPosition赋值,它的location是0,然后size是3,stride是5个float,起始指针是vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), vertices);
glEnableVertexAttribArray(0);
// 给vTexCoor赋值,它的location是1,然后size是2,stride是5个float,起始指针是vertices+3
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), vertices + 3);
glEnableVertexAttribArray(1);// 5. 绘制
glDrawArrays(GL_TRIANGLES, 0, 6); // 绘制两个三角形,一共是6个点

4. 效果图

由于放大的关系,过渡的颜色信息都是插值生成的,颜色分别为红、绿、蓝、黄四个像素的 2*2 大小的纹理放大后的效果如下:

四、工程地址

上面没有列出的代码,可以在下面地址中找到:

https://github.com/afei-cn/OpenGLSample/tree/master/texturedemo

另外,这是一个可运行的 Android App 工程。上例中使用的是 C++ 的 API,还有一个使用 Java API 加载图片纹理的例子也在上面的地址中,效果图如下:

最后,一些 OpenGL 的相关文章: https://blog.csdn.net/afei__/article/category/8502759

OpenGL ES 之 2D 纹理介绍和使用相关推荐

  1. OpenGL ES之十——纹理贴图(展示一张图片)

    概述 这是一个系列的Android平台下OpenGl ES介绍,从最基本的使用最终到VR图的展示的实现,属于基础篇.(后面针对VR视频会再有几篇文章,属于进阶篇) OpenGL ES之一--概念扫盲 ...

  2. android纹理坐标,Android OpenGL ES 7.1纹理绘制

    基本原理 与渐变色接近,但有些区别: 渐变色:光栅化过程中,计算出颜色值,然后在片段着色器的时候可以直接赋值 纹理:光栅化过程中,计算出当前片段在纹理上的坐标位置,然后在片段着色器的中,根据这个纹理上 ...

  3. OpenGL教程 用2D图形介绍OpenGL

    OpenGL教程 用2D图形介绍OpenGL 1.设置OpenGL 要设置OpenGL,取决于您的编程平台,请阅读: 如何在C / C ++中编写OpenGL程序. 如何在Java中编写OpenGL程 ...

  4. 关于OpenGL ES中的纹理压缩

    基于OpenGL ES的压缩纹理有常见的如下几种实现: 1. ETC1(Ericcson texture compression) 2. PVRTC(PowerVR texture compressi ...

  5. 使用opengl es编写2d游戏的一些说明和技巧

    即使是制作2d游戏,在移动设备上也往往使用opengl es绘图接口,来达到较高的绘制效率.这里记录一下我学习opengl es 2d绘图过程中逐渐明白的一些原理与技巧. opengl 3d贴图的基本 ...

  6. OpenGL ES绘制3D纹理贴图

    最近看了<疯狂android讲义>的图形相关的内容,结合自己的理解,整理了一下. 下图是做出来的3D纹理贴图效果,手指在屏幕滑动时,图片可以随之转动. 要实现一个纹理贴图,很简单,大致需要 ...

  7. OpenGL ES 详解纹理生成和纹理映射步骤以及函数

    通常一个纹理映射的步骤是: 创建纹理对象.就是获得一个新的纹理句柄 ID. 指定纹理.就是将数据赋值给 ID 的纹理对象,在这一步,图像数据正式加载到了 ID 的纹理对象中. 设定过滤器.定义了ope ...

  8. android opengl es 纹理 不同设备 白色,android – OpenGL ES 2.0纹理没有在某些设备上显示...

    早上好,这是2个纹理非幂的典型例子. 由于多种原因,纹理在分辨率上需要2的幂,这是一个非常常见的错误,每个人都碰巧陷入这个陷阱:)我也是. 2个纹理的非功率在某些设备/ GPU上运行平稳的事实,仅仅取 ...

  9. Android OpenGL ES 应用(二) 纹理

    上一篇讲了基础入门 OpenGL (一) ,这一次主要学习OpenGL 纹理基本学习总结 要是做复杂的OpenGL应用程序,一定会用到纹理技术.纹理说白了就是把图片或者视频图像绘制到OpenGL空间中 ...

  10. android 使用OPENGL ES实现三角形纹理贴图效果-纹理映射基础

    效果图:...... 编写Dad.java *在Dad构造器中创建和设置场景渲染器为主动渲染,并设置重写触屏时间回调方法以记录触控笔坐标,改变三角形坐标系的位置,使三角形能够在场景中转动 *为声明场景 ...

最新文章

  1. 破解石碑(区间动规)
  2. matplotlib 显示批量图片_chapter4-1 简单数据可视化包Matplotlib整理1
  3. SAP Spartacus 里 ng-template和ng-container的嵌套使用
  4. tinyid 教程_tinyid
  5. java 并发 面试_Java 并发基础常见面试题总结
  6. 第一节: Timer的定时任务的复习、Quartz.Net的入门使用、Aop思想的体现
  7. Perfect:Swift 语言服务器端软件框架简介
  8. shell脚本命令行参数里的空白符
  9. 手撕源码之SpringApplication.java【Spring Boot 2.4.4】
  10. Windows - 安装/卸载服务 - 学习/实践
  11. springboot分层构建Docker镜像实践,统统都会!
  12. DIM-00014: 无法打开windows nt服务控制管理器
  13. Mongodb之Chunk研究
  14. 开始使用 Elasticsearch (1)
  15. Java实习面试重点基础知识
  16. 图书信息管理系统(三)
  17. Linux 监控工具之Cacti使用详解(一)
  18. css3倒数两个,:nth-last-child()
  19. 安卓手机如何快速投屏到windows(10/8.1/7)电脑上
  20. Spring源码解析十五

热门文章

  1. 嵌入式怎么入门,嵌入式应该先学习什么
  2. vue——微信二维码显示vue-qr/qrcodejs2
  3. 怎样自建邮件服务器,简单快速搭建邮件服务器
  4. 中职学校计算机课听课记录表,中职听课记录
  5. 前端练习:day02
  6. 微信服务号运营的八大策略
  7. Ubuntu双显卡切换方法总结
  8. 各纬度气候分布图_世界主要气候类型分布模式图39330
  9. N+1个数据恢复软件,全中文!全破解免费!(潘中医)_-Chaz-_新浪博客
  10. 外卖行业现状分析_外卖行业生存现状分析:你还会点外卖吗?