Opengl ES系列学习--FBO拷贝texture实现滤镜效果

2023-12-19 20:32

本文主要是介绍Opengl ES系列学习--FBO拷贝texture实现滤镜效果,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

     本节我们来学习一下如何使用FBO,FBO的一些基本内容网上有很多的介绍资料,我们这里就不多说了。把使用方法总结出来,方便大家学习。我们也使用FBO实现了一些最简单的滤镜效果,录屏如下。

     本节的内容是实时的相机预览,有部分核心的代码不方便发出来,滤镜的效果很简单,都是直接在片段着色器中修改RGB值完成的。网上也有使用Lut表查表实现的,大家可以参考:滤镜Shader--LookUpTable,好像github也需要翻墙了,在家里都无法打开了。

     本节实现的相机预览效果,上面的大图是一个GLSurfaceView,下面的一排小框是一个GLSurfaceView,两个GLSurfaceView对应各自的Opengl环境,上面的大框我们就不讲了,就是打开相机,拿到实时的预览数据,然后在Render类中的onDrawFrame方法中渲染出来。

     我们来看下面一排小框,它的Render类是FilterRender,完整源码如下:

package com.hong.camera;import android.content.Context;
import android.hardware.HardwareBuffer;
import android.media.Image;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.util.Log;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;import static android.opengl.GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
import static android.opengl.GLES20.GL_CLAMP_TO_EDGE;
import static android.opengl.GLES20.GL_COLOR_ATTACHMENT0;
import static android.opengl.GLES20.GL_FRAMEBUFFER;
import static android.opengl.GLES20.GL_FRAMEBUFFER_COMPLETE;
import static android.opengl.GLES20.GL_LINEAR;
import static android.opengl.GLES20.GL_RGBA;
import static android.opengl.GLES20.GL_TEXTURE1;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_WRAP_S;
import static android.opengl.GLES20.GL_TEXTURE_WRAP_T;
import static android.opengl.GLES20.GL_UNSIGNED_BYTE;
import static android.opengl.GLES20.glBindFramebuffer;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glCheckFramebufferStatus;
import static android.opengl.GLES20.glFramebufferTexture2D;
import static android.opengl.GLES20.glGenFramebuffers;
import static android.opengl.GLES20.glGenTextures;
import static android.opengl.GLES20.glTexImage2D;
import static android.opengl.GLES20.glTexParameterf;
import static android.opengl.GLES20.glTexParameteri;
import static android.opengl.GLES20.glUniform1f;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.GLES30.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES30.GL_TEXTURE0;
import static android.opengl.GLES30.glActiveTexture;
import static android.opengl.GLES30.glClear;
import static android.opengl.GLES30.glDisableVertexAttribArray;
import static android.opengl.GLES30.glEnableVertexAttribArray;
import static android.opengl.GLES30.glGetUniformLocation;
import static android.opengl.GLES30.glUniform1i;
import static android.opengl.GLES30.glUseProgram;
import static android.opengl.GLES30.glViewport;
import static android.opengl.Matrix.rotateM;
import static android.opengl.Matrix.setIdentityM;public class FilterRender implements GLSurfaceView.Renderer {private static final String TAG = FilterRender.class.getSimpleName();private static final int BYTES_PER_FLOAT = 4;private static final int BYTES_PER_SHORT = 2;private static final int POSITION_COMPONENT_SIZE = 3;private static final int COLOR_COMPONENT_SIZE = 4;private final float[] mVerticesData ={-1.0f, 1f, 0.0f, // v0-1.0f, -1f, 0.0f, // v11.0f, -1f, 0.0f,  // v2-1.0f, 1f, 0.0f, // v01.0f, -1f, 0.0f,  // v21.0f, 1f, 0.0f,  // v3};private final float[] mTexturePosiontData ={0.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f, 1.0f,1.0f, 0.0f,};private Context mContext;private int mProgramObject, mFilterProgramObject;private int vPosition, aTexturePosition, mOESTextureUniform, uMatrixUniform;private int vFilterPosition, aFilterTexturePosition, uTexture, uFilterMatrixUniform, uIndex, uTime;private int mWidth, mHeight;private int mTextureId;private FloatBuffer mVertices, mTextureBuffer;private final float[] viewMatrix = new float[16];private final float[] filterMatrix = new float[16];int[] mFBO = new int[1];int[] mFBOTexture = new int[1];private int itemWidth, itemHeight, itemGap;private long lStartTime = 0;public FilterRender(Context context) {mContext = context;mVertices = ByteBuffer.allocateDirect(mVerticesData.length * BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();mVertices.put(mVerticesData).position(0);mTextureBuffer = ByteBuffer.allocateDirect(mTexturePosiontData.length * BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();mTextureBuffer.put(mTexturePosiontData).position(0);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {createOriginProgram();createFilterProgram();}private void createOriginProgram() {String vShaderStr = ESShader.readShader(mContext, "vertexShader.glsl");String fShaderStr = ESShader.readShader(mContext, "fragmentShader.glsl");int vertexShader;int fragmentShader;int[] linked = new int[1];// Load the vertex/fragment shadersvertexShader = ESShader.loadShader(GLES30.GL_VERTEX_SHADER, vShaderStr);fragmentShader = ESShader.loadShader(GLES30.GL_FRAGMENT_SHADER, fShaderStr);// Create the program objectmProgramObject = GLES30.glCreateProgram();if (mProgramObject == 0) {return;}GLES30.glAttachShader(mProgramObject, vertexShader);GLES30.glAttachShader(mProgramObject, fragmentShader);// Link the programGLES30.glLinkProgram(mProgramObject);// Check the link statusGLES30.glGetProgramiv(mProgramObject, GLES30.GL_LINK_STATUS, linked, 0);if (linked[0] == 0) {Log.e(TAG, "Error linking program:");Log.e(TAG, GLES30.glGetProgramInfoLog(mProgramObject));GLES30.glDeleteProgram(mProgramObject);return;}// Store the program objectvPosition = GLES30.glGetAttribLocation(mProgramObject, "vPosition");aTexturePosition = GLES30.glGetAttribLocation(mProgramObject, "aTexturePosition");mOESTextureUniform = glGetUniformLocation(mProgramObject, "mOESTextureUniform");uMatrixUniform = glGetUniformLocation(mProgramObject, "uMatrix");Log.e(TAG, "get vPosition: " + vPosition + ", mOESTexture: " + mOESTextureUniform+ ", aTexturePosition: " + aTexturePosition);int[] textureId = new int[1];glGenTextures(1, textureId, 0);mTextureId = textureId[0];setIdentityM(viewMatrix, 0);}private void createFilterProgram() {mFilterProgramObject = OpenGLUtils.loadProgram(mContext, R.raw.filter_vertex, R.raw.filter_fragment);if (mFilterProgramObject == 0) {return;}// Store the program objectvFilterPosition = GLES30.glGetAttribLocation(mFilterProgramObject, "vPosition");aFilterTexturePosition = GLES30.glGetAttribLocation(mFilterProgramObject, "aTexturePosition");uTexture = glGetUniformLocation(mFilterProgramObject, "uTexture");uFilterMatrixUniform = glGetUniformLocation(mFilterProgramObject, "uMatrix");uIndex = glGetUniformLocation(mFilterProgramObject, "uIndex");uTime = glGetUniformLocation(mFilterProgramObject, "uTime");Log.e(TAG, "get vPosition: " + vPosition + ", mOESTexture: " + mOESTextureUniform+ ", aTexturePosition: " + aTexturePosition);glGenFramebuffers(1, mFBO, 0);glGenTextures(1, mFBOTexture, 0);setIdentityM(filterMatrix, 0);rotateM(filterMatrix, 0, -90, 0f, 0f, 1f);}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {mWidth = width;mHeight = height;itemWidth = 144;itemHeight = 192;itemGap = 30;Log.e(TAG, "onSurfaceChanged, " + mWidth + ", " + mHeight);}@Overridepublic void onDrawFrame(GL10 gl) {if (lStartTime == 0) {lStartTime = System.currentTimeMillis();}glClear(GL_COLOR_BUFFER_BIT);GLES30.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);drawFBO();drawFilter();}private void drawFBO() {glBindFramebuffer(GL_FRAMEBUFFER, mFBO[0]);glBindTexture(GL_TEXTURE_2D, mFBOTexture[0]);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, itemWidth, itemHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);glBindTexture(GL_TEXTURE_2D, mFBOTexture[0]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBOTexture[0], 0);drawOES();glBindFramebuffer(GL_FRAMEBUFFER, 0);}private void drawOES() {...}private void drawFilter() {glUseProgram(mFilterProgramObject);glUniformMatrix4fv(uFilterMatrixUniform, 1, false, filterMatrix, 0);GLES30.glVertexAttribPointer(vFilterPosition, 3, GLES30.GL_FLOAT, false, 0, mVertices);glEnableVertexAttribArray(vFilterPosition);GLES30.glVertexAttribPointer(aFilterTexturePosition, 2, GLES30.GL_FLOAT, false, 0, mTextureBuffer);glEnableVertexAttribArray(aFilterTexturePosition);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, mFBOTexture[0]);glUniform1i(uTexture, 1);glUniform1f(uTime, System.currentTimeMillis() - lStartTime);for (int i = 0; i < 9; i++) {glViewport((itemWidth + itemGap) * i, 0, itemWidth, itemHeight);glUniform1i(uIndex, i);GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 6);}glDisableVertexAttribArray(vFilterPosition);glDisableVertexAttribArray(aFilterTexturePosition);}
}

     该类中也包含了FBO的使用方法,也就是drawFBO方法的逻辑,和普通的texture一样,我们在使用之前必须先调用glBindFramebuffer把生成的目标fbo对象绑定到GL_FRAMEBUFFER  target上,当然target类型还有GL_READ_FRAMEBUFFER、GL_DRAW_FRAMEBUFFER两种,不过大部分场景下我们没有必须区分,直接使用GL_FRAMEBUFFER就可以了;接下来的几步我们需要声明一张texture 2d,并调用glTexImage2D将它的内存置空,这张2d纹理就是用来向fbo拷贝数据的,fbo本身是没有存储空间的,它是需要关联一张颜色附件才能正常使用,我们使用FBO执行纹理拷贝时,其实就是把源纹理中的数据拷贝到这张和FBO关联的2d纹理附件中,最后要使用的也是这张关联的2d纹理附件,这才是FBO的正确使用方法,大家必须把这点搞清楚,否则还是没有掌握FBO的使用方法;设定好2d texture的纹理参数后,调用glFramebufferTexture2D将该2d纹理附着到FBO上,到这里FBO的使用环境就算是准备好,我们也可以调用glCheckFramebufferStatus对FBO的状态进行验证,状态正常时返回GL_FRAMEBUFFER_COMPLETE(36053),否则我们就需要检查FBO哪些条件没有满足了。

     上面的环境准备好之后,我们就可以执行正常的绘制了,也就是drawOES方法的逻辑,drawOES方法中是和普通绘制完全相同的逻辑,使用OES纹理绘制我们相机的预览数据,绘制完成后调用glBindFramebuffer(GL_FRAMEBUFFER, 0)把FBO解绑,解绑的意思其实就是把和FBO关联的纹理id恢复成默认的0。

     这里是不是觉得有点奇怪了,先准备了FBO的环境,然后正常绘制,最后解绑,好像第二步我们的正常绘制和FBO没有进行任何拷贝相关的动作,那么预览数据是如何到FBO对象中的?这也就是FBO的奥秘了。FBO就是Opengl给我们准备好的一张画布,默认的FBO纹理id是0,也就是显示屏,如果我们调用glBindFramebuffer把默认的0替换成其他值,那么这时候就不会显示到屏幕上了,这就是离屏渲染的叫法,大家可以试下,在正常绘制的开始调用glBindFramebuffer把FBO的默认id改掉,屏幕就会黑了,什么东西都绘制不出来,就是这个原因。这是我们本节讲FBO的第二点重要内容,上面绑定一张颜色附件是第一点重要内容。

     理解了这两点,我们也就掌握了FBO的使用原则了,再回头看我们的三个方法:drawFBO是准备FBO的使用环境,环境准备好之后,调用drawOES;因为上一步一开始就调用glBindFramebuffer,改变了默认的FBO的纹理id,所以此时的drawOES都是在往FBO绑定的那张颜色附件的2d纹理中绘制的,也就是说这一步执行完,那张关联在FBO上的2d纹理附件就有数据了!!!第三步drawFilter也就是最简单的Opengl绘制了,因为纹理已经有数据了,我们就只需要调用正常的Opengl流程,把这张2d纹理绘制出来就可以了。要实现我们前面的那些滤镜效果,因我们已经把相机的预览原数据拷贝过来了,剩下的只需要for循环绘制多次,然后针对每一次的绘制改变fragmentshader中的颜色采样就可以了,这块的道理应该都很简单,我们通过阅读片段着色器的源码来结束本节的内容,谢谢大家!!

#version 300 esprecision mediump float;uniform sampler2D uTexture;
in vec2 vTexturePosition;
out vec4 o_fragColor;
uniform int uIndex;
uniform float uTime;vec4 filterProcess(vec4 color) {vec4 outColor = vec4(0.0);switch (uIndex) {case 0:float c = (color.r + color.g + color.b) / 3.0;outColor = vec4(c, c, c, color.a);break;case 1:outColor = color + vec4(0.2, 0.2, 0.0, 0.0);break;case 2:outColor = color + vec4(0.0, 0.0, 0.3, 0.0);break;case 3:float dis = 0.01;color += texture(uTexture, vec2(vTexturePosition.x-dis, vTexturePosition.y-dis));color += texture(uTexture, vec2(vTexturePosition.x-dis, vTexturePosition.y+dis));color += texture(uTexture, vec2(vTexturePosition.x+dis, vTexturePosition.y-dis));color += texture(uTexture, vec2(vTexturePosition.x+dis, vTexturePosition.y+dis));color += texture(uTexture, vec2(vTexturePosition.x-dis, vTexturePosition.y-dis));color += texture(uTexture, vec2(vTexturePosition.x-dis, vTexturePosition.y+dis));color += texture(uTexture, vec2(vTexturePosition.x+dis, vTexturePosition.y-dis));color += texture(uTexture, vec2(vTexturePosition.x+dis, vTexturePosition.y+dis));color += texture(uTexture, vec2(vTexturePosition.x-dis, vTexturePosition.y-dis));color += texture(uTexture, vec2(vTexturePosition.x-dis, vTexturePosition.y+dis));color += texture(uTexture, vec2(vTexturePosition.x+dis, vTexturePosition.y-dis));color += texture(uTexture, vec2(vTexturePosition.x+dis, vTexturePosition.y+dis));color /= 13.0;outColor = color;break;case 4:float lightUpValue = abs(sin(uTime / 1000.0)) / 4.0;vec4 addColor = vec4(lightUpValue, lightUpValue, lightUpValue, 1.0);outColor = color + addColor;break;default :outColor = color;break;}return outColor;
}void main() {vec4 color = texture(uTexture, vTexturePosition);o_fragColor = filterProcess(color);
}

 

这篇关于Opengl ES系列学习--FBO拷贝texture实现滤镜效果的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/513606

相关文章

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一