OpenGL ES 纹理(7)
OpenGL ES 纹理(7)
简述
通过前面几章的学习,我们已经可以绘制渲染我们想要的逻辑图形了,但是如果我们想要渲染一张本地图片,这就需要纹理了。
纹理其实是一个可以用于采样的数据集,比较典型的就是图片了,我们知道我们的片段着色器会对每一个像素都执行一次来计算,该像素应该渲染什么颜色,纹理就是一个数据集,比如想要渲染一个图片,我们就是用图片的所有像素信息作为总数据集,然后片段着色器计算的时候就根据像素坐标去图片纹理数据集中取出对应的像素。
这一节我们就通过纹理来渲染一张图片。
接口介绍
- glGenTextures 申请分配纹理
public static native void glGenTextures(int n, int[] textures, int offset);
第一个参数为需要分配纹理个数,第二个申请的纹理句柄(是一个出参),第三个是偏移 - glTexParameteri 配置纹理参数
public static native void glTexParameteri(int target, int pname, int param);
第一个参数是目标,一般使用GL_TEXTURE_2D,第二和第三个参数分别是需要配置的参数的名称和值。
下面我们列举几个常用的参数:- GL_TEXTURE_MAG_FILTER 当显示区域大于原来纹理时,如何放大图像
- GL_NEAREST 会使用靠近的像素作为扩增的像素(计算量小但是效果差)
- GL_LINEAR 求附近像素的加权平均值
- GL_TEXTURE_MIN_FILTER 当显示区域小于原来纹理时,如何缩小图像,GL_NEAREST, GL_LINEAR值和GL_TEXTURE_MAG_FILTER类似。
- GL_TEXTURE_WRAP_T / GL_TEXTURE_WRAP_S
- 纹理的ST坐标系,对应XY坐标,纹理的坐标范围是0-1,这个参数就是控制如果超出这个坐标范围的表现。GL_CLAMP会使用边缘拉伸,GL_REPEAT会重复使用图像填充
- GL_TEXTURE_MAG_FILTER 当显示区域大于原来纹理时,如何放大图像
- GLUtils.texImage2D 加载纹理
用于使用位图来填充纹理 - glGenerateMipmap 开启多级细节。我们看越远的东西就会越小,这时候可能不需要加载完整的图像,可以降低图像细节提高性能。
纹理坐标系
我们之前提过,OpenGL ES的坐标系是(-1,-1)->(1,1)
在OpenGL ES中,纹理的坐标系是(0,0)-> (1,1)的,且纹理的起始坐标(0,0)是在左上角。
纹理渲染图片
原图
顶点数据
顶点数据和绘制正方形时候类似,依旧是4个顶点,然后索引缓冲区中6个点,以两个三角形拼接成一个正方形。
其中每个顶点有5个数据,前三个作为OpenGL的坐标,后两个作为纹理坐标,这里坐标对应关系是OpenGLES坐标系(-0.5,-0.5)对应纹理坐标系(1, 1),即OpenGL ES左下角对应图片的右下角,最终效果是图片顺时针旋转来90度。
private float[] vertexArray = new float[] {-0.5f, -0.5f, 0.0f, 1, 1f,0.5f, -0.5f, 0.0f, 1f, 0f,-0.5f, 0.5f, 0.0f, 0f, 1f,0.5f, 0.5f, 0.0f, 0f, 0f,
};private short[] indexArray = new short[] {0,1,2,1,2,3
};
着色器
顶点着色器有两个属性,一个vPosition为OpenGL ES坐标,另一个vCoordinate为纹理坐标。纹理坐标会通过varying变量outCoordinate透传给片段着色器。
片段着色器中有一个统一变量vTexture,类型为sampler2D,它相当于纹理的句柄,我门通过texture2D方法,传入纹理句柄和当前纹理坐标即可获取当前坐标的颜色。
private final String vertexShaderCode ="attribute vec4 vPosition;" +"attribute vec2 vCoordinate;" +"varying vec2 outCoordinate;" +"void main() {" +" gl_Position = vPosition;" +" outCoordinate = vCoordinate;" +"}";private final String fragmentShaderCode ="precision mediump float;" +"varying vec2 outCoordinate;" +"uniform sampler2D vTexture;" +"void main() {" +" gl_FragColor = texture2D(vTexture,outCoordinate);" +"}";
顶点数据填充并加载纹理
顶点填充数据逻辑和孩子i去哪都一样,这里额外调用来一个loadTexture(getContext(), R.drawable.test),R.drawable.test是我们的需要显示的图片资源,我们来看看loadTexture里面做了什么。
public void onSurfaceCreated(GL10 gl, EGLConfig config) {// 清除颜色GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// 创建顶点缓冲区int[] idBuffer = new int[2];GLES30.glGenBuffers(2, idBuffer, 0);vertexBufferId = idBuffer[0];elementBufferId = idBuffer[1];// 顶点缓冲区数据填充FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexArray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();vertexBuffer.put(vertexArray);vertexBuffer.position(0);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertexArray.length * 4,vertexBuffer,GLES30.GL_STATIC_DRAW);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);ShortBuffer indexBuffer = ByteBuffer.allocateDirect(indexArray.length * 4).order(ByteOrder.nativeOrder()).asShortBuffer();indexBuffer.put(indexArray);indexBuffer.position(0);GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId);GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,indexArray.length * 4,indexBuffer,GLES30.GL_STATIC_DRAW);GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);loadTexture(getContext(), R.drawable.test);// shadershaderProgramId = initShaderProgram(vertexShaderCode, fragmentShaderCode);
}
loadTexture
这里调用的方法我们在之前都已经介绍过了,这个过程其实和创建顶点缓冲区很想,先申请一个纹理,申请纹理会返回一个纹理句柄,然后绑定纹理,配置参数,配置纹理对应的位图,再解绑纹理,后续就可以通过这个纹理句柄来使用这个纹理了。
public int loadTexture(final Context context, final int resourceId) {GLES30.glGenTextures(1, textureHandle, 0);if (textureHandle[0] != 0) {// 绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureHandle[0]);// 设置纹理参数GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);// 加载纹理Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId);GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0);// 生成多级细节GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);// 释放Bitmap资源bitmap.recycle();}// 解绑纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);return textureHandle[0];
}
顶点布局以及渲染
渲染的流程和之前类似,处理配置顶点布局以外,这里主要新增的逻辑就是需要通过glBindTexture绑定纹理,还有将纹理句柄通过统一变量传给片段着色器。
public void onDrawFrame(GL10 gl) {// 清除屏幕GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);// 使能着色器程序GLES30.glUseProgram(shaderProgramId);// 绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureHandle[0]);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);int positionLocation = GLES30.glGetAttribLocation(shaderProgramId, "vPosition");GLES30.glEnableVertexAttribArray(positionLocation);GLES30.glVertexAttribPointer(positionLocation, 3, GLES30.GL_FLOAT, false, 5 * 4, 0);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);int coordinateLocation = GLES30.glGetAttribLocation(shaderProgramId, "vCoordinate");GLES30.glEnableVertexAttribArray(coordinateLocation);GLES30.glVertexAttribPointer(coordinateLocation, 2, GLES30.GL_FLOAT, false, 5 * 4, 3 * 4);// 配置纹理句柄到统一变量中去int vTextureLocation = GLES30.glGetUniformLocation(shaderProgramId, "vTexture");GLES30.glUniform1f(vTextureLocation, textureHandle[0]);GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId);// 调用DrawCall绘制三角形GLES30.glDrawElements(GLES30.GL_TRIANGLES, 6, GLES30.GL_UNSIGNED_SHORT, 0);// 清除配置GLES30.glDisableVertexAttribArray(positionLocation);GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);GLES30.glUseProgram(0);
}
效果
如预期一样,这里相比原图顺时针旋转了90度。
小结
本节通过演示如何使用纹理加载渲染一个图片来介绍了纹理的使用方式,除此之外还通过纹理坐标系和OpenGL ES坐标系的映射关系将图片做了一个旋转,其实我们还有一个方式专门来做图像的变换,就是变化矩阵,我们下一节会专门来介绍这个。
相关文章:

OpenGL ES 纹理(7)
OpenGL ES 纹理(7) 简述 通过前面几章的学习,我们已经可以绘制渲染我们想要的逻辑图形了,但是如果我们想要渲染一张本地图片,这就需要纹理了。 纹理其实是一个可以用于采样的数据集,比较典型的就是图片了,我们知道我…...

【C#】CacheManager:高效的 .NET 缓存管理库
在现代应用开发中,缓存是提升性能和降低数据库负载的重要技术手段。无论是 Web 应用、桌面应用还是移动应用,缓存都能够帮助减少重复的数据查询和处理,从而提高系统的响应速度。然而,管理缓存并不简单,尤其是当你需要处…...

【数学分析笔记】第4章第2节 导数的意义和性质(2)
4. 微分 4.2 导数的意义与性质 4.2.3 单侧导数 f ′ ( x ) lim Δ x → 0 f ( x Δ x ) − f ( x ) Δ x lim x → x 0 f ( x ) − f ( x 0 ) x − x 0 f(x)\lim\limits_{\Delta x\to 0}\frac{f(x\Delta x)-f(x)}{\Delta x}\lim\limits_{x\to x_0}\frac{f(x)-f(x_0)…...

深度学习:迁移学习
目录 一、迁移学习 1.什么是迁移学习 2.迁移学习的步骤 1、选择预训练的模型和适当的层 2、冻结预训练模型的参数 3、在新数据集上训练新增加的层 4、微调预训练模型的层 5、评估和测试 二、迁移学习实例 1.导入模型 2.冻结模型参数 3.修改参数 4.创建类ÿ…...

Footprint Growthly Quest 工具:赋能 Telegram 社区实现 Web3 飞速增长
作者:Stella L (stellafootprint.network) 在 Web3 的快节奏世界里,社区互动是关键。而众多 Web3 社区之所以能够蓬勃发展,很大程度上得益于 Telegram 平台。正因如此,Footprint Analytics 精心打造了 Growthly —— 一款专为 Tel…...
进入xwindows后挂起键盘鼠标没有响应@FreeBSD
问题: 在升级pkg包后,系统无法进入xfce等xwindows,表现为黑屏和看见鼠标,左上角有一个白字符块,键盘鼠标没有反应,整个系统卡住。但是可以ssh登录,内部的服务一切正常。 表现 处理过程…...
CentOS7.9 snmptrapd更改162端口
端口更改前: 命令: netstat -an |grep 162 [root@kibana snmp]# netstat -an | grep 162 udp 0 0 0.0.0.0:162 0.0.0.0:* unix 3 [ ] STREAM CONNECTED 45162 /run/systemd/journal/stdout u…...

模糊测试SFuzz亮相第32届中国国际信息通信展览会
9月25日,被誉为“中国ICT市场的创新基地和风向标”的第32届中国国际信息通信展在北京盛大开幕,本次展会将在为期三天的时间内,为信息通信领域创新成果、尖端技术和产品提供国家级交流平台。开源网安携模糊测试产品及相关解决方案精彩亮相&…...
CMake学习
向大佬lyf学习,先把其8服务器中所授fine 文章目录 前言一、CMakeList.txt 命令1. 最外层CMakeLists1.1 cmake_minimum_required()1.2 project()1.3 set()1.4 add_subdirectory(&…...

书生·浦语大模型全链路开源开放体系
书生浦语大模型全链路开源开放体系 大模型应用生态的发展和繁荣是建立在模型基座强大的通用基础能力之上的。上海AI实验室联合团队研究认为,大模型各项性能提升的基础在于语言建模能力的增强,对于大模型的研究应回归语言建模本质,通过更高质量…...
PHP安装swoole扩展无效,如何将文件上传至Docker容器
目录 过程 操作方式 过程 在没有使用过云服务器以前,Docker这个平台一直都很神秘。在我申请了华为云服务器,并使用WordPress镜像去搭建自己的网站以后,我不得不去把Docker平台弄清楚,原因是我使用的一个主题需要安装swoole扩展,才能够正常启用。而要将swoole.so这个扩展…...
Web3.0 应用项目
Web3.0 是下一代互联网的概念,旨在去中心化、用户拥有数据控制权和通过区块链技术实现信任的网络。Web3.0的应用项目主要集中在区块链、加密货币、去中心化应用 (DApps)、去中心化金融 (DeFi)、NFT(非同质化代币)等领域。以下是一些典型的 We…...
Linux 学习笔记(十六)—— 重定向与缓冲区
一、文件重定向 矩阵的下标,也就是文件描述符的分配规则,是从0开始空的最小的文件描述符分配给进程新打开的文件;文件输出重定向的原理是,关掉1(输出),然后打开文件,这个新打开的文…...

828华为云征文|WordPress部署
目录 前言 一、环境准备 二、远程连接 三、WordPress简介 四、WordPress安装 1. 基础环境安装 编辑 2. WordPress下载与解压 3. 创建站点 4. 数据库配置 总结 前言 WordPress 是一个非常流行的开源内容管理系统(Content Management System, CMS…...

华为开源自研AI框架昇思MindSpore应用案例:计算高效的卷积模型ShuffleNet
如果你对MindSpore感兴趣,可以关注昇思MindSpore社区 ShuffleNet ShuffleNet网络介绍 ShuffleNetV1是旷视科技提出的一种计算高效的CNN模型,和MobileNet, SqueezeNet等一样主要应用在移动端,所以模型的设计目标就是利用有限的计算资源来达到…...
《C++ 小游戏:简易飞机大战游戏的实现》
文章目录 《C 游戏代码解析:简易飞机大战游戏的实现》一、游戏整体结构与功能概述二、各个类和函数的功能分析(一)BK类 - 背景类(二)hero_plane类 - 玩家飞机类(三)plane_bullet类 - 玩家飞机发…...

SpringCloud源码:服务端分析(二)- EurekaServer分析
背景 从昨日的两篇文章:SpringCloud源码:客户端分析(一)- SpringBootApplication注解类加载流程、SpringCloud源码:客户端分析(二)- 客户端源码分析。 我们理解了客户端的初始化,其实…...

插槽slot在vue中的使用
介绍 在 Vue.js 中,插槽(slot)是一种用于实现组件内容分发的功能。通过插槽,可以让父组件在使用子组件时自定义子组件内部的内容。插槽提供了一种灵活的方式来组合和复用组件。 项目中有很多地方需要调用一个组件,比…...

针对考研的C语言学习(定制化快速掌握重点2)
1.C语言中字符与字符串的比较方法 在C语言中,单字符可以用进行比较也可以用 > , < ,但是字符串却不能用直接比较,需要用strcmp函数。 strcmp 函数的原型定义在 <string.h> 头文件中,其定义如下: int strcmp(const …...
[C++][IO流][流输入输出][截断理解]详细讲解
目录 1.流输入输出说明1.<<执行顺序2.>>执行顺序 2.截断(trunc)理解 1.流输入输出说明 1.<<执行顺序 链式操作的顺序:当使用多个<<操作符进行链式插入时,执行顺序是从左到右的 每个<<操作都将数据插入到前一个流的输出中…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...