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);
}

六.注意点

6.1 片段着色器中采样器的使用

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

uniform samplerExternalOES sCameraTexture;

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

uniform sampler2D sampler; 

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

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

6.2 Render、GLSurfaceView等实现过程

Render在GLSurfaceView中的设置,还有GLSurfeaceView在Activity或Fragment中的加载,这些常规操作的代码就不再做演示了。

根据自己实际开发过程嵌入实现就行。

七.最终效果

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

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

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

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

相关文章

精华回顾:Web3 前沿创新者在 DESTINATION MOON 共话未来

9 月 17 日,由 TinTinLand 主办的「DESTINATION MOON: Web3 Dev Summit Shanghai 2023」线下活动在上海黄浦如约而至。 本次 DESTINATION MOON 活动作为 2023 上海区块链国际周的 Side Event,设立了 4 场主题演讲与 3 个圆桌讨论,聚集了诸多…

Goby 漏洞发布|Revive Adserver 广告管理系统 adxmlrpc.php 文件远程代码执行漏洞(CVE-2019-5434)

漏洞名称:Revive Adserver 广告管理系统 adxmlrpc.php 文件远程代码执行漏洞(CVE-2019-5434) English Name: Revive Adserver adxmlrpc.php Remote Code Execution Vulnerability (CVE-2019-5434) CVSS core: 9.0 影响资产数&a…

【自学记录】深度学习入门——基于Python的理论与实现(第3章 神经网络)

3.4.3 3层神经网络Python实现 实现的是这个网络 **init_network()**函数会进行权重和偏置的初始化,并将它们保存在字典变量network中。这个字典变量network中保存了每一层所需的参数(权重和偏置)。 **forward()**函数中则封装了将输入信号转换为输出信号的处理过程…

ReadPaper论文阅读工具

之前看文献一直用的EndNote嘛,但是突然发现了它的一个弊端,就是说每次没看完退出去之后,下次再接着看的时候它不能保留我上一次的位置信息,又要重头开始翻阅,这让我感到很烦躁哈哈。(当然也不知道是不是我哪…

MySQL数据库简介+库表管理操作+数据库用户管理

Mysql Part 1 一、数据库的基本概念1.1 使用数据库的必要性1.2 数据库基本概念1.2.1 数据(Data)1.2.2 表1.2.3 数据库1.2.4 数据库管理系统(DBMS)1.2.5 数据库系统 1.3 数据库的分类1.3.1 关系数据库 SQL1.3.2 非关系数据库 NoSQL…

C语言自定义类型详解(1)结构体知识汇总

本篇概要 本篇主要讲述C语言结构体的相关知识,包括结构体的基本声明,结构体的匿名结构,结构体的自引用,结构体变量的定义和初始化以及结构体的内存对齐等相关知识。 文章目录 本篇概要1.结构体1.1结构体的基本声明1.2结构体的特殊…

[SQL Server]在应使用条件的上下文(在 ‘)‘ 附近)中指定了非布尔类型的表达式,查询时间大于某个数值时

这种条件查询条件里面不要有空格&#xff0c;一个也不要有 这种条件查询条件里面不要有空格&#xff0c;一个也不要有 $giftsDb::table(drawgot)->where(disabtime,<,"2030-01-03")->select();

合肥先进光源国家重大科技基础设施项目及配套工程启动会纪念

合肥先进光源国家重大科技基础设施项目及配套工程启动会纪念 卡西莫多 合肥长丰岗集里 肥鸭从此别泥塘 先平场地设围栏 进而工地筑基忙 光阴似箭指日争 源流汇智山水长 国器西北扩新地 家校又添新区园 重器托举有群力 大步穿梭两地间 科教兴邦大国策 技术盈身坦荡行…

【数据结构】—交换排序之快速排序究极详解,手把手带你从简单的冒泡排序升级到排序的难点{快速排序}(含C语言实现)

食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f525;这就不得不推荐此专栏了&#xff1a;C语言 ♈️今日夜电波&#xff1a;靴の花火—ヨルシカ 0:28━━━━━━️&#x1f49f;──────── 5:03 …

Eureka服务器注册

一。Eureka服务器注册 1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://mav…

Android之MediaMetricsService实现本质(四十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

移动端H5封装一个 ScrollList 横向滚动列表组件,实现向左滑动

效果&#xff1a; 1.封装组件&#xff1a; <template><div class"scroll-list"><divclass"scroll-list-content":style"{ background, color, fontSize: size }"ref"scrollListContent"><div class"scroll…

Python150题day08

2.基础语法篇 2.1 if 条件句 ①单个条件分支 使用input函数接收用户的输入&#xff0c;如果用户输入的整数是偶数&#xff0c;则使用print函数输出"你输入的整数是:{value],它是偶数”&#xff0c;[value]部分要替换成用户的输入。 解答: value input("请输⼊⼀…

Jmeter——结合Allure展示测试报告

在平时用jmeter做测试时&#xff0c;生成报告的模板&#xff0c;不是特别好。大家应该也知道allure报告&#xff0c;页面美观。 先来看效果图&#xff0c;报告首页&#xff0c;如下所示&#xff1a; 报告详情信息&#xff0c;如下所示&#xff1a; 运行run.py文件&#xff0c;…

在React中,什么是组件的状态(state)?如何更新组件的状态?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 创建和初始化状态⭐ 更新状态⭐ 注意事项⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前…

【PyTorch攻略(2/7)】 加载数据集

一、说明 PyTorch提供了两个数据原语&#xff1a;torch.utils.data.DataLoader和torch.utils.data.Dataset&#xff0c;允许您使用预加载的数据集以及您自己的数据。数据集存储样本及其相应的标签&#xff0c;DataLoader 围绕数据集包装一个可迭代对象&#xff0c;以便轻松访问…

C++ 共享内存相关的API

C 共享内存相关的API 1.什么是共享内存1.共享内存的概念2.共享内存的原理3.共享内存使用注意点 2.共享内存有关API的操作函数及示例1.新建共享内存-shmget2.连接共享内存到当前的地址空间-shnat3.当前进程分离共享内存shmdt4.控制共享内存-shmctl5.共享内存操作示例 3.共享内存…

Node2Vec实战---《悲惨世界》人物图嵌入

1. pip各个包后导入 import networkx as nx # 图数据挖掘 import numpy as np # 数据分析 import random # 随机数# 数据可视化 import matplotlib.pyplot as plt %matplotlib inline plt.rcParams[font.sans-serif][SimHei] # 用来正常显示中文标签 plt.rcParams[axes.uni…

定制SQLmap和WAF绕过

1. SQLmap tamper 脚本编写 以sqli-lab第26关为例 输入?id1’ --&#xff0c;报错字符型注入 考虑闭合问题&#xff0c;输入?id1’ and 1&#xff0c;但是回显中and和空格消失了&#xff0c;可知and和空格被过滤了 因为and和or被过滤考虑使用双写绕过手段&#xff0c;空格使…

Linux常用命令—find命令大全

文章目录 一、find命令常用功能1、find命令的基本信息如下。2、按照文件名搜索3、按照文件大小搜索4、按照修改时间搜索5、按照权限搜索举例&#xff1a;6、按照所有者和所属组搜索7、按照文件类型搜索8、逻辑运算符 一、find命令常用功能 1、find命令的基本信息如下。 命令名…