OpenGLES:单纹理贴图

一.概述

最近疏于写博客,接下来会陆续更新这段时间OpenGLES的一些开发过程。

前两篇OpenGLES的博客讲解了怎样使用OpenGLES实现相机普通预览多宫格滤镜

在相机实现过程中,虽然使用到了纹理,但只是在生成一个纹理之后,使用纹理去创建SurfaceTexture,然后再用SurfaceTexture创建Surface,并没有使用纹理进行图片渲染,也就是纹理贴图。

之前的博文《OpenGL:纹理》中使用OpenGL实现了正方形箱子的纹理贴图,而且也详细介绍了纹理的生成、纹理坐标、设置过滤、环绕方式等。

本篇博客要讲解是OpenGLES的纹理贴图,两者原理其实是一样的,但是本篇博客更偏向于实际应用。

以下主要是代码演示,跟随代码注释了解实现过程。

二.Render类:

public class ImgRender implements GLSurfaceView.Renderer {private final String TAG = ImgRender.class.getSimpleName();private final Context mContext;private float vertexData[] = {-1f, -1f,  //左下1f, -1f,   //右下-1f, 1f,   //左上1f, 1f,    //右上};//Android 纹理原点在左上角private float textureData[] = {0.0f, 1.0f,  //左上1.0f, 1.0f,  //右上0.0f, 0.0f,  //左下1.0f, 0.0f,  //右下};//shader程序/渲染器private int shaderProgram;//纹理idprivate int[] textureId = new int[1];//顶点坐标private int aPosition;//纹理坐标private int aTexCoord;//采样器private int sampler;//顶点数据缓存private FloatBuffer vertexBuffer;//纹理数据缓存private FloatBuffer textureBuffer;//一个顶点有几个数据private int VERTEX_POSITION_SIZE = 2;//一个纹理点有几个数据private int TEXTURE_POSITION_SIZE = 2;public ImgRender(Context context) {mContext = context;}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {Log.v(TAG, "onSurfaceCreated()");glClearColor(0.0f, 0.0f, 0.0f, 1.0f);initGLES();}public void initGLES() {Log.v(TAG, "initGLES!");/************** 着色器程序/渲染器 **************///创建并连接 着色器程序shaderProgram = ShaderUtils.createAndLinkProgram(mContext,"img_vertex_shader.glsl","img_fragtment_shader.glsl");if (shaderProgram == 0) {Log.v(TAG, "create And Link ShaderProgram Fail!");return;}//使用着色器源程序glUseProgram(shaderProgram);/************** 着色器变量 **************///从着色器程序中获取各个变量aPosition = glGetAttribLocation(shaderProgram, "aPosition");aTexCoord = glGetAttribLocation(shaderProgram, "aTexCoord");sampler = glGetUniformLocation(shaderProgram, "sampler");//将片段着色器的采样器(纹理属性:sampler)设置为0号单元glUniform1i(sampler, 0);vertexBuffer = ShaderUtils.getFloatBuffer(vertexData);textureBuffer = ShaderUtils.getFloatBuffer(textureData);//创建纹理对象glGenTextures(textureId.length, textureId, 0);TextureUtils.LoadTexture(mContext, textureId[0], R.drawable.bg2);}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {Log.v(TAG, "onSurfaceChanged(): " + width + " x " + height);glViewport(0, 0, width, height);}@Overridepublic void onDrawFrame(GL10 gl) {//Log.v(TAG, "onDrawFrame()");glClear(GL_COLOR_BUFFER_BIT);/********* 顶点操作 **********/glEnableVertexAttribArray(aPosition);glEnableVertexAttribArray(aTexCoord);glVertexAttribPointer(aPosition, VERTEX_POSITION_SIZE, GL_FLOAT, false, 2 * 4, vertexBuffer);glVertexAttribPointer(aTexCoord, TEXTURE_POSITION_SIZE, GL_FLOAT, false, 2 * 4, textureBuffer);/********* 纹理操作:激活、绑定 **********/glActiveTexture(GL_TEXTURE);glBindTexture(GL_TEXTURE_2D, textureId[0]);/********* 绘制 **********///绘制vertexData.length/2即4个点glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexData.length / 2);/********* 纹理操作:解除绑定 **********/glBindTexture(GL_TEXTURE_2D, 0);//关闭顶点数组的句柄glDisableVertexAttribArray(aPosition);glDisableVertexAttribArray(aTexCoord);}
}

三.ShaderUtils相关函数:

3.1 createAndLinkProgram()

    /** 创建和链接着色器程序* 参数:顶点着色器、片段着色器程序ResId* 返回:成功创建、链接了顶点和片段着色器的着色器程序Id*/public static int createAndLinkProgram(Context context, String vertexShaderFN, String fragShaderFN) {//创建着色器程序int shaderProgram = glCreateProgram();if (shaderProgram == 0) {Log.e(TAG, "Failed to create shaderProgram ");return 0;}//获取顶点着色器对象int vertexShader = loadShader(GL_VERTEX_SHADER, loadShaderSource(context, vertexShaderFN));if (0 == vertexShader) {Log.e(TAG, "Failed to load vertexShader");return 0;}//获取片段着色器对象int fragmentShader = loadShader(GL_FRAGMENT_SHADER, loadShaderSource(context, fragShaderFN));if (0 == fragmentShader) {Log.e(TAG, "Failed to load fragmentShader");return 0;}//绑定顶点着色器到着色器程序glAttachShader(shaderProgram, vertexShader);//绑定片段着色器到着色器程序glAttachShader(shaderProgram, fragmentShader);//链接着色器程序glLinkProgram(shaderProgram);//检查着色器链接状态int[] linked = new int[1];glGetProgramiv(shaderProgram, GL_LINK_STATUS, linked, 0);if (linked[0] == 0) {glDeleteProgram(shaderProgram);Log.e(TAG, "Failed to link shaderProgram");return 0;}return shaderProgram;}

3.2 getFloatBuffer()

    public static FloatBuffer getFloatBuffer(float[] array) {//将顶点数据拷贝映射到 native 内存中,以便opengl能够访问FloatBuffer buffer = ByteBuffer.allocateDirect(array.length * BYTES_PER_FLOAT)//直接分配 native 内存,不会被gc.order(ByteOrder.nativeOrder())//和本地平台保持一致的字节序(大/小头).asFloatBuffer();//将底层字节映射到FloatBuffer实例,方便使用buffer.put(array)//将顶点拷贝到 native 内存中.position(0);//每次 put position 都会 + 1,需要在绘制前重置为0return buffer;}

四.TextureUtils相关函数

4.1 LoadTexture()

    //纹理Id由外部传入public static void LoadTexture(Context context, int textureId, int bitmapResId) {//绑定纹理:将纹理放到当前单元的 GL_TEXTURE_BINDING_EXTERNAL_OES 目标对象中glBindTexture(GL_TEXTURE_2D, textureId);//配置纹理:过滤方式glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);/************* bitmap **************///获取图片的 bitmapBitmap bitmap = BitmapFactory.decodeResource(context.getResources(), bitmapResId);//绑定 bitmap 到textureIds[1]纹理GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);bitmap.recycle();//用完及时回收//解绑纹理 指的是离开对 纹理的配置,进入下一个状态glBindTexture(GL_TEXTURE_2D, 0);}

五.着色器代码

5.1 img_vertex_shader.glsl

#version 300 eslayout (location = 0) in vec4 aPosition;         //把顶点坐标给这个变量, 确定要画画的形状
layout (location = 1) in vec4 aTexCoord;         //接收纹理坐标,接收采样器采样图片的坐标//传给片元着色器 像素点
out vec2 vTexCoord;void main()
{//内置变量 gl_Position ,我们把顶点数据赋值给这个变量 opengl就知道它要画什么形状了gl_Position = aPosition;vTexCoord = aTexCoord.xy;
}

5.2 img_fragtment_shader.glsl

#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;in vec2 vTexCoord; //纹理坐标,图片当中的坐标点uniform sampler2D sampler;  //图片,采样器out vec4 outColor;void main(){outColor = texture(sampler, vTexCoord);
}

六.Render、GLSurfaceView等实现过程

GLRender及其在GLSurfaceView中的设置:

mGLSurfaceView = rootView.findViewById(R.id.gl_SurfaceView);
//设置GLES版本
mGLSurfaceView.setEGLContextClientVersion(3);
//创建Render对象,并将其设置到GLSurfaceView
mImgRender = new ImgRender(getActivity());
mGLSurfaceView.setRenderer(mImgRender);
mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);

还有GLSurfeaceView在Activity或Fragment中的加载,这些常规操作的代码就不再详细演示了。根据自己实际开发过程实现就行。

七.注意点

1.片段着色器中采样器的使用

有一点需要注意的是,在之前实现相机预览的片段着色器代码中,使用的采样器是:

uniform samplerExternalOES sCameraTexture;

现在渲染图片时使用的采样器是:

uniform sampler2D sampler; 

在实现OpenGLES纹理贴图过程中,最初直接延用了实现相机预览时的采样器samplerExternalOES,但是图片始终无法渲染成功。

这是因为"采样器:samplerExternalOES"是OpenGLES中专门用来采样YUV数据的,所以在实现相机预览时要使用它,但是渲染图片时,就要用常规的"采样器Sampler2D"了。

八.最终效果

使用了一张宇宙星空图,请忽略"打开应用,点击按钮"过程

最后那张宇宙星空图的呈现,就是本次OpenGLES博文所要实现的纹理贴图的最终效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/140495.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vuepress+gitee免费搭建个人在线博客(无保留版)

文章目录 最终效果,一睹为快!一、工具选型二、什么是VuePress三、准备工作3.1 node 安装3.2 Git安装3.3 Gitee账号注册 四、搭建步骤4.1 初始化VuePress4.2 安装VuePress4.3 初始化目录4.4 编写文章 五、部署到Gitee5.1 创建仓库5.2 个人空间地址设置4.3…

【JVM内存区域及创建对象的过程】

文章目录 JVM内存区域及创建对象的过程JVM内存区域JDK1.6、1.7、1.8内存区域的变化?创建对象的过程类的声明周期: JVM内存区域及创建对象的过程 JVM内存区域 JVM 内存区域最粗略的划分可以分为 堆 和栈,当然,按照虚拟机规范&…

2023华为杯数学建模竞赛E题

一、前言 颅内出血(ICH)是由多种原因引起的颅腔内出血性疾病,既包括自发性出血,又包括创伤导致的继发性出血,诊断与治疗涉及神经外科、神经内科、重症医学科、康复科等多个学科,是临床医师面临的重要挑战。…

Linux 操作技巧

目录 一、shell-命令解释器 二、Linux中的特殊符号 三、命令历史--history 一、shell-命令解释器 shell——壳,命令解释器,负责解析用户输入的命令 ——内置命令(shell内置) ——外置命令,在文件系统的某个目录下&…

如何取消显示Notepad++每行显示的CRLF符号

新电脑中重新安装了Nodepad,打开记事本后发现出现了许多黑底的CR|LF标记,特别碍眼。 如何取消呢? 视图 -> 显示符号 -> 取消勾选 显示行尾符操作步骤 预期效果

SpringBoot结合Vue.js+axios框架实现增删改查功能+网页端实时显示数据库数据(包括删除多条数据)

本文适用对象:已有基础的同学,知道基础的SpringBoot配置和Vue操作。 在此基础上本文实现基于SpringBoot和Vue.js基础上的增删改查和数据回显、刷新等。 一、实时显示数据库数据 实现步骤: 第1步:编写动态请求响应类&#xff1a…

Ubuntu上线一个JAVA环境微服务架构的系统

项目介绍 项目背景: 已经有一套系统,迁移部署到新服务器,并使用不同数据,相同框架,提供对应业务服务 单机测试,从裸机-系统安装-软件架构-部署-数据迁移-发版-上线,整体流程与思路分享,包含后端、数据,测试、网络、运维等相关事务。 项目目的: 部署并迁移系统,…

linux下CentOS安装mysql-5.7

linux下安装mysql只需要在root用户下安装,普通用户也能使用 1.检查: 通过以下两条命令查看改系统下是否已存在mysql。 ps ajx | grep mysql ps ajx | grep mariadb通过指令如果只显示如下两条信息,则当前系统下不存在MySQL。 就可以直接进…

【初阶数据结构】二叉树全面知识总结

二叉树详解 树的概念及其结构树的概念树的相关概念树的表示方法孩纸兄弟表示法双亲表示法(并查集) 树的实际应用 二叉树二叉树的概念二叉树的种类二叉树的性质二叉树的存储结构 二叉树顺序结构的实现堆的概念及结构堆向上、向下调整法堆的插入堆的删除堆…

华为云云耀云服务器L实例评测 | 使用Docker快速搭建博客系统

使用Docker快速搭建wordpress博客系统. 文章目录 使用Docker快速搭建wordpress博客系统.需要了解部署与管理工具介绍安装Docker配置镜像加速器下载镜像创建数据库容器创建wordpress博客容器访问博客初始化配置博客扩展和管理 WordPress总结 需要了解 本文主要讲述快速搭建自己的…

Apache Doris 快速入门

1. 基本概念 FE,Frontend,前端节点,接收用户查询请求,SQL解析,执行计划生成,元数据管理,节点管理等 BE,Backend,后端节点,数据存储,执行查询计划…

[npm]脚手架本地全局安装1

[npm]脚手架本地全局安装1 npm link 全局安装npm install 全局安装卸载全局安装的脚手架 该文章是你的脚手架已经开发完成的前提下,你想要本地全局安装该脚手架,便于本地使用脚手架的命令的情况 npm link 全局安装 如果本地开发的项目是个脚手架&#…

Redis学习笔记--002

Redis的JAVA客户端 文章目录 Redis的JAVA客户端一、Redis的Java客户端的种类二、Jedis2.1、使用步骤2.2、Jedis连接池 三、[SpringDataRedis](https://spring.io/projects/spring-data-redis)3.1、介绍3.2、RedisTemplate3.3、SpringDataRedis使用步骤3.4、SpringDataRedis的序…

10.1网站编写(Tomcat和servlet基础)

一.Tomcat: 1.Tomcat是java写的,运行时需要依赖jre,所以要装jdk. 2.建议配置好环境变量. 3.默认端口号8080(业务端口)可能会被占用,建议改一下(本人改成了9999). 4.另一个默认端口是8005(管理端口). 二Servlet基础(编写一个hello world代码): 整体分为7个步骤,分别是创建…

linux安装mysql8.0

最近在云服务器上部署服务,需要在linux端安装mysql8以上的版本,出现一些问题,记录一下。 1.首先卸载以前mysql版本。 (1)停止mysql服务。 systemctl stop mysqld.service (2)删除对应程序与文件。 查看安装…

什么是机器学习?

所谓的机器学习就是让机器去学习, 在这里关键词是学习两个字 在我们传统的学习算法的过程中, 我们真正要做的事情其实是让机器去执行,也就是说设计一个算法, 去让机器具体的完成一个任务,而不是学习一个任务 最早的机器…

JumpServer开源堡垒机与爱可生云树数据库完成兼容性认证

近日,中国领先的开源软件提供商FIT2CLOUD飞致云宣布,JumpServer开源堡垒机已经完成与爱可生云树数据库软件的兼容性认证。经过双方联合测试,云树数据库软件(简称:ActionDB)V1.0与杭州飞致云信息科技有限公司…

雷达编程实战之静态杂波滤除与到达角估计

雷达中经过混频的中频信号常常混有直流分量等一系列硬件设计引入的固定频率杂波,我们称之位静态杂波,雷达信号处理需要把这些静态杂波滤除从而有效的提高信噪比,实现准确的目标检测功能。 目标的到达角估计作为常规车载雷达信号处理的末端&am…

针对敏感数据的安全转录服务

即便在新冠肺炎疫情期间,继续保持了最高级别的机密性 新冠肺炎疫情带来的各种限制向所有服务提供商提出了挑战,促使提供商们想方设法采取更富想象力的新方法来满足客户的需求。澳鹏采用了一种由两种方案组成的工作机制,服务于客户机密材料的…

解决Spring Boot 2.7.16 在服务器显示启动成功无法访问问题:从本地到服务器的部署坑

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…