android平台下OpenGL ES 3.0给图片添加黑白滤镜
OpenGL ES 3.0学习实践
- android平台下OpenGL ES 3.0从零开始
- android平台下OpenGL ES 3.0绘制纯色背景
- android平台下OpenGL ES 3.0绘制圆点、直线和三角形
- android平台下OpenGL ES 3.0绘制彩色三角形
- android平台下OpenGL ES 3.0从矩形中看矩阵和正交投影
- android平台下OpenGL ES 3.0着色语言基础知识(上)
- android平台下OpenGL ES 3.0着色语言基础知识(下)
- android平台下OpenGL ES 3.0实例详解顶点属性、顶点数组
- android平台下OpenGL ES 3.0实例详解顶点缓冲区对象(VBO)和顶点数组对象(VAO)
- android平台下OpenGL ES 3.0绘制立方体的几种方式
- android平台下OpenGL ES 3.0实现2D纹理贴图显示bitmap
- android平台下OpenGL ES 3.0基于GLSurfaceView对相机Camera预览实时处理
- android平台下OpenGL ES 3.0基于TextureView对相机Camera预览实时处理
- android平台下基于ANativeWindow实现渲染bitmap图像
- android平台下OpenGL ES 3.0给图片添加黑白滤镜
概述
之前了解的一些第三方库,可以对视频预览页面进行各种滤镜切换,比较神奇,于是自己研究了一番,基础原理倒也不是很难,我们先从最简单的图片开始处理,下面记录一下过程。
配置环境
操作系统:ubuntu 16.05
效果预览
实现方案
笔者这里封装了一个渲染控件OpenGLView
,继承自GLSurfaceView
/*** @anchor: andy* @date: 2019-03-27* @description:*/
public class OpenGLView extends GLSurfaceView {private FilterRenderer mGLRender;public OpenGLView(Context context) {this(context, null);}public OpenGLView(Context context, AttributeSet attrs) {super(context, attrs);setupSurfaceView();}private void setupSurfaceView() {//设置版本setEGLContextClientVersion(3);mGLRender = new FilterRenderer();setRenderer(mGLRender);try {requestRender();} catch (Exception e) {e.printStackTrace();}}/*** 设置滤镜* 滤镜由于可能存在多种类型* 这里抽象了一个基础的滤镜类* queueEvent** @param baseFilter*/public void setFilter(final BaseFilter baseFilter) {queueEvent(new Runnable() {@Overridepublic void run() {if (mGLRender != null) {mGLRender.setFilter(baseFilter);}}});try {requestRender();} catch (Exception e) {e.printStackTrace();}}
}
FilterRenderer的实现:实现了GLSurfaceView.Renderer接口
/*** @anchor: andy* @date: 2018-11-09* @description: */
public class FilterRenderer implements GLSurfaceView.Renderer {private static final String TAG = "FilterRenderer";private int mSurfaceWidth, mSurfaceHeight;private BaseFilter mTargetFilter = new OriginFilter();@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {mTargetFilter.onSurfaceCreated();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {mSurfaceWidth = width;mSurfaceHeight = height;mTargetFilter.onSurfaceChanged(width, height);}@Overridepublic void onDrawFrame(GL10 gl) {mTargetFilter.onDrawFrame();}public void setFilter(BaseFilter baseFilter) {if (mTargetFilter != null) {mTargetFilter.onDestroy();}mTargetFilter = baseFilter;if (mTargetFilter != null) {mTargetFilter.onSurfaceCreated();mTargetFilter.onSurfaceChanged(mSurfaceWidth, mSurfaceHeight);}}}
由于目前的想法是实现一个简单的黑白滤镜,其实直接在片段着色器中硬编码
,也可以可以实现的,但是后续的扩展性可能难以保证,笔者这里是抽象了一个基础的BaseFilter
滤镜实现类,后续的滤镜都是基于这个扩展而来。
抽象类BaseFilter的实现:子类可以扩展不同的滤镜
/*** @anchor: andy* @date: 2019-03-27* @description:*/
public abstract class BaseFilter implements RendererFilter {private static final String TAG = "RendererFilter";private FloatBuffer vertexBuffer, mTexVertexBuffer;private ShortBuffer mVertexIndexBuffer;protected int mProgram;private int textureId;/*** 顶点坐标* (x,y,z)*/private float[] POSITION_VERTEX = new float[]{0f, 0f, 0f, //顶点坐标V01f, 1f, 0f, //顶点坐标V1-1f, 1f, 0f, //顶点坐标V2-1f, -1f, 0f, //顶点坐标V31f, -1f, 0f //顶点坐标V4};/*** 纹理坐标* (s,t)*/private static final float[] TEX_VERTEX = {0.5f, 0.5f, //纹理坐标V01f, 0f, //纹理坐标V10f, 0f, //纹理坐标V20f, 1.0f, //纹理坐标V31f, 1.0f //纹理坐标V4};/*** 索引*/private static final short[] VERTEX_INDEX = {0, 1, 2, //V0,V1,V2 三个顶点组成一个三角形0, 2, 3, //V0,V2,V3 三个顶点组成一个三角形0, 3, 4, //V0,V3,V4 三个顶点组成一个三角形0, 4, 1 //V0,V4,V1 三个顶点组成一个三角形};private int uMatrixLocation;/*** 矩阵*/private float[] mMatrix = new float[16];/*** 顶点着色器*/private String mVertexShader;/*** 片段着色器*/private String mFragmentShader;/*** 加载默认的着色器*/public BaseFilter() {this(ResReadUtils.readResource(R.raw.no_filter_vertex_shader), ResReadUtils.readResource(R.raw.no_filter_fragment_shader));}public BaseFilter(final String vertexShader, final String fragmentShader) {mVertexShader = vertexShader;mFragmentShader = fragmentShader;//初始化内存空间setupBuffer();}private void setupBuffer() {//分配内存空间,每个浮点型占4字节空间vertexBuffer = ByteBuffer.allocateDirect(POSITION_VERTEX.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();//传入指定的坐标数据vertexBuffer.put(POSITION_VERTEX);vertexBuffer.position(0);mTexVertexBuffer = ByteBuffer.allocateDirect(TEX_VERTEX.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(TEX_VERTEX);mTexVertexBuffer.position(0);mVertexIndexBuffer = ByteBuffer.allocateDirect(VERTEX_INDEX.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer().put(VERTEX_INDEX);mVertexIndexBuffer.position(0);}public void setupProgram() {//编译着色器final int vertexShaderId = ShaderUtils.compileVertexShader(mVertexShader);final int fragmentShaderId = ShaderUtils.compileFragmentShader(mFragmentShader);//链接程序片段mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);uMatrixLocation = GLES30.glGetUniformLocation(mProgram, "u_Matrix");//加载纹理textureId = TextureUtils.loadTexture(AppCore.getInstance().getContext(), R.drawable.main);LogUtils.d(TAG, "program=%d matrixLocation=%d textureId=%d", mProgram, uMatrixLocation, textureId);}@Overridepublic final void onSurfaceCreated() {//设置背景颜色GLES30.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);//初始化程序对象setupProgram();}@Overridepublic final void onSurfaceChanged(int width, int height) {GLES30.glViewport(0, 0, width, height);final float aspectRatio = width > height ?(float) width / (float) height :(float) height / (float) width;if (width > height) {//横屏Matrix.orthoM(mMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);} else {//竖屏Matrix.orthoM(mMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);}}@Overridepublic void onDrawFrame() {GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);//使用程序片段GLES30.glUseProgram(mProgram);//更新属性等信息onUpdateDrawFrame();GLES30.glUniformMatrix4fv(uMatrixLocation, 1, false, mMatrix, 0);GLES30.glEnableVertexAttribArray(0);GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);GLES30.glEnableVertexAttribArray(1);GLES30.glVertexAttribPointer(1, 2, GLES30.GL_FLOAT, false, 0, mTexVertexBuffer);GLES30.glActiveTexture(GLES30.GL_TEXTURE0);//绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId);// 绘制GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTEX_INDEX.length, GLES20.GL_UNSIGNED_SHORT, mVertexIndexBuffer);}public void onUpdateDrawFrame() {//供子类实现}@Overridepublic void onDestroy() {GLES30.glDeleteProgram(mProgram);}}
笔者这里以黑白滤镜为例:
/*** @anchor: andy* @date: 2019-03-27* @description:*/
public class GrayFilter extends BaseFilter {private int aFilterLocation;private float[] filterValue = new float[]{0.299f, 0.587f, 0.114f};public GrayFilter() {super(ResReadUtils.readResource(R.raw.gray_filter_vertex_shader), ResReadUtils.readResource(R.raw.gray_filter_fragment_shader));}@Overridepublic void setupProgram() {super.setupProgram();//获取顶点着色器中滤镜统一变量的位置aFilterLocation = GLES30.glGetUniformLocation(mProgram, "a_Filter");}@Overridepublic void onUpdateDrawFrame() {//更新参数GLES30.glUniform3fv(aFilterLocation, 1, filterValue, 0);}}
黑白滤镜的着色器
实现:
顶点着色器:
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;uniform mat4 u_Matrix;
uniform vec3 a_Filter;out vec3 vFilter;
out vec2 vTexCoord;
void main() {gl_Position = u_Matrix * vPosition;vTexCoord = aTextureCoord;vFilter = a_Filter;
}
片段着色器:
#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;//传入滤镜数据
in vec3 vFilter;
in vec2 vTexCoord;//输出
out vec4 vFragColor;
void main() {vec4 vTextureColor = texture(uTextureUnit,vTexCoord);//计算此颜色的灰度值float vFilterColor = (vFilter.x * vTextureColor.r + vFilter.y * vTextureColor.g + vFilter.z * vTextureColor.b);vFragColor = vec4(vFilterColor, vFilterColor, vFilterColor, 1.0);
}
灰度值与RGB的计算公式:
Y = 0.299R + 0.587G + 0.114*B
滤镜切换
在对应的菜单选择中,我们动态切换即可:
@Overridepublic boolean onOptionsItemSelected(MenuItem item) {int itemId = item.getItemId();if (itemId == R.id.filter_default) {mGlView.setFilter(new OriginFilter());} else if (itemId == R.id.filter_gray) {mGlView.setFilter(new GrayFilter());}return super.onOptionsItemSelected(item);}
小结
每次切换滤镜的时候,都会销毁当前的滤镜,删除相关的程序对象
,然后重新构建新的滤镜实例
,加载相关的着色器,链接到程序对象
中,完成滤镜的切换。
项目地址:sample-filter
https://github.com/byhook/opengles4android
参考:
https://blog.csdn.net/xiaxl/article/details/72622236
https://github.com/wuhaoyu1990/MagicCamera
android平台下OpenGL ES 3.0给图片添加黑白滤镜相关推荐
- android平台下OpenGL ES 3.0绘制圆点、直线和三角形
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- android平台下OpenGL ES 3.0绘制纯色背景
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- android平台下OpenGL ES 3.0从零开始
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- android平台下OpenGL ES 3.0使用GLSurfaceView对相机Camera预览实时处理
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- android平台下OpenGL ES 3.0实例详解顶点属性、顶点数组
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- android平台下OpenGL ES 3.0绘制立方体的几种方式
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)
第一篇 Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜) 第二篇 Android Camera使用OpenGL ES 2.0和T ...
- Android Camera使用OpenGL ES 2.0和TextureView对预览进行实时二次处理(黑白滤镜)
本系列教程会有三篇文章讲解Android平台滤镜的实现方式,希望在阅读本文之前先阅读下述第一篇文档,因为第一篇讲过的知识,本文并不会细讲了. 第一篇 Android Camera使用OpenGL ES ...
- Android平台下OpenGL初步
Android OpenGL ES 开发教程 从入门到精通 http://blog.csdn.net/zhoudailiang/article/details/50176143 http://blog ...
最新文章
- 我的 CSS3 笔记
- flask框架中勾子函数的使用
- 历史习题与答案解析(1-50期)
- tp5 聚合max获取不到string最大值_深入理解Kafka客户端之如何获取集群元数据
- C# 简单连接数据库并执行SQL查询语句
- matlab进化树的下载,mega7.0进化树软件下载-mega 7.0 win 64位下载【附详细使用教程】 - 百当下载站...
- 视频转码(Java)
- git原理学习记录:从基本指令到背后原理,实现一个简单的git
- WORD排版-目录管理/标题排版
- 揭秘腾讯智慧城市版图:“数字政府”+“超级大脑”的新打法
- 【WINDOWS / DOS 批处理】for命令详解(一)
- Azure BareMetal 裸金属
- 医院微信系统服务器故障,80%的医院微信都有问题
- afrog 发布新版本 Release 1.3.5 真的想你
- 定制你的语音识别-并行语音识别解码空间
- 交叉验证的几个方法的解释(简单交叉验证、k折交叉验证、留一法)
- 计算机网络:P4.2-网络层(中)
- Java Client/Server 上传文件到服务器与保存文件到本地
- 摩托罗拉等六厂商联手 开发通用Linux手机平台
- 瞻仰了一下Gavin King的风采