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给图片添加黑白滤镜相关推荐

  1. android平台下OpenGL ES 3.0绘制圆点、直线和三角形

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  2. android平台下OpenGL ES 3.0绘制纯色背景

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  3. android平台下OpenGL ES 3.0从零开始

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  4. 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绘 ...

  5. android平台下OpenGL ES 3.0实例详解顶点属性、顶点数组

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  6. android平台下OpenGL ES 3.0绘制立方体的几种方式

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  7. Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)

    第一篇 Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜) 第二篇 Android Camera使用OpenGL ES 2.0和T ...

  8. Android Camera使用OpenGL ES 2.0和TextureView对预览进行实时二次处理(黑白滤镜)

    本系列教程会有三篇文章讲解Android平台滤镜的实现方式,希望在阅读本文之前先阅读下述第一篇文档,因为第一篇讲过的知识,本文并不会细讲了. 第一篇 Android Camera使用OpenGL ES ...

  9. Android平台下OpenGL初步

    Android OpenGL ES 开发教程 从入门到精通 http://blog.csdn.net/zhoudailiang/article/details/50176143 http://blog ...

最新文章

  1. 我的 CSS3 笔记
  2. flask框架中勾子函数的使用
  3. 历史习题与答案解析(1-50期)
  4. tp5 聚合max获取不到string最大值_深入理解Kafka客户端之如何获取集群元数据
  5. C# 简单连接数据库并执行SQL查询语句
  6. matlab进化树的下载,mega7.0进化树软件下载-mega 7.0 win 64位下载【附详细使用教程】 - 百当下载站...
  7. 视频转码(Java)
  8. git原理学习记录:从基本指令到背后原理,实现一个简单的git
  9. WORD排版-目录管理/标题排版
  10. 揭秘腾讯智慧城市版图:“数字政府”+“超级大脑”的新打法
  11. 【WINDOWS / DOS 批处理】for命令详解(一)
  12. Azure BareMetal 裸金属
  13. 医院微信系统服务器故障,80%的医院微信都有问题
  14. afrog 发布新版本 Release 1.3.5 真的想你
  15. 定制你的语音识别-并行语音识别解码空间
  16. 交叉验证的几个方法的解释(简单交叉验证、k折交叉验证、留一法)
  17. 计算机网络:P4.2-网络层(中)
  18. Java Client/Server 上传文件到服务器与保存文件到本地
  19. 摩托罗拉等六厂商联手 开发通用Linux手机平台
  20. 瞻仰了一下Gavin King的风采

热门文章

  1. 微信头像失效_微信公众号头像链接判断是否有效
  2. java爬虫实战——实现简单的爬取网页数据
  3. 【猿如意】中的『Bluefish』工具详情介绍
  4. html避免多次点击选中页面文字或者内容时出现蓝色背景
  5. vue 视频播放(使用vue-video-player)
  6. 数学基础知识-排列与组合
  7. 解决页面间体验问题的纯前端容器Lath
  8. Battery_MSCCC:基于MATLAB/Simulink的具有多级(5级)恒流控制的电池充放电仿真模型
  9. 一根网线搞定树莓派可视化界面
  10. md文件的简洁打开方式