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.<<执行顺序 链式操作的顺序:当使用多个<<操作符进行链式插入时,执行顺序是从左到右的 每个<<操作都将数据插入到前一个流的输出中…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...

Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...