Android OpenGLES如何给相机添加滤镜详解

Android OpenGLES如何给相机添加滤镜详解,第1张

概述Android OpenGLES如何给相机添加滤镜详解 滤镜介绍 目前市面上的滤镜有很多,但整体归类也就几样,都是在fragment shader中进行处理.目前滤镜最常用的就是 lut滤镜以及调整RGB曲线的滤镜了.其他的类型变更大同小异. 动态滤镜的构建 为了实现动态下载的滤镜,我们接下来实现一套滤镜的json参数,主要包括滤镜类型.滤镜名称.vertex shader.fragment shader 文件.统一变量列表.与统一变量绑定的纹理图片.默认滤镜强度.是否带纹理宽高偏移量.音乐路径.音乐是否循环播放等参数. json 以及各个字段的介绍

滤镜介绍

目前市面上的滤镜有很多,但整体归类也就几样,都是在fragment shader中进行处理。目前滤镜最常用的就是 lut滤镜以及调整RGB曲线的滤镜了。其他的类型变更大同小异。

动态滤镜的构建

为了实现动态下载的滤镜,我们接下来实现一套滤镜的Json参数,主要包括滤镜类型、滤镜名称、vertex shader、fragment shader 文件、统一变量列表、与统一变量绑定的纹理图片、默认滤镜强度、是否带纹理宽高偏移量、音乐路径、音乐是否循环播放等参数。

Json 以及各个字段的介绍如下:

{ "filterList": [{  "type": "filter",// 表明滤镜类型,目前filter是只普通滤镜,后续还会加入其它类型的滤镜  "name": "amaro",// 滤镜名称  "vertexShader": "",// vertex shader 文件名  "fragmentShader": "fragment.glsl",// fragment shader 文件名  "uniformList":["blowoutTexture","overlayTexture","mapTexture"],// 统一变量  "uniformData": { // 与统一变量绑定的纹理图片   "blowoutTexture": "blowout.png","overlayTexture": "overlay.png","mapTexture": "map.png"  },"strength": 1.0,// 默认滤镜强度 0.0 ~ 1.0之间  "texelOffset": 0,// 是否需要支持宽高偏移值,即需要传递 1.0f/wIDth,1.0f/height到shader中  "audioPath": "",// 音乐路径  "audioLooPing": 1  // 是否循环播放音乐 }]}

有了Json 之后,我们需要解码得到滤镜参数对象,解码如下:

/**  * 解码滤镜数据  * @param folderPath  * @return  */ public static Dynamiccolor decodeFilterData(String folderPath)   throws IOException,JsONException {  file file = new file(folderPath,"Json");  String filterjson = fileUtils.convertToString(new fileinputStream(file));  JsONObject JsonObject = new JsONObject(filterjson);  Dynamiccolor dynamiccolor = new Dynamiccolor();  dynamiccolor.unzipPath = folderPath;  if (dynamiccolor.filterList == null) {   dynamiccolor.filterList = new ArrayList<>();  }  JsONArray filterList = JsonObject.getJsONArray("filterList");  for (int filterIndex = 0; filterIndex < filterList.length(); filterIndex++) {   DynamiccolorData filterData = new DynamiccolorData();   JsONObject JsonData = filterList.getJsONObject(filterIndex);   String type = JsonData.getString("type");   // Todo 目前滤镜只做普通的filter,其他复杂的滤镜类型后续在做处理   if ("filter".equals(type)) {    filterData.name = JsonData.getString("name");    filterData.vertexShader = JsonData.getString("vertexShader");    filterData.fragmentShader = JsonData.getString("fragmentShader");    // 获取统一变量字段    JsONArray uniformList = JsonData.getJsONArray("uniformList");    for (int uniformIndex = 0; uniformIndex < uniformList.length(); uniformIndex++) {     String uniform = uniformList.getString(uniformIndex);     filterData.uniformList.add(uniform);    }    // 获取统一变量字段绑定的图片资源    JsONObject uniformData = JsonData.getJsONObject("uniformData");    if (uniformData != null) {     Iterator<String> dataIterator = uniformData.keys();     while (dataIterator.hasNext()) {      String key = dataIterator.next();      String value = uniformData.getString(key);      filterData.uniformDataList.add(new DynamiccolorData.UniformData(key,value));     }    }    filterData.strength = (float) JsonData.getDouble("strength");    filterData.texelOffset = (JsonData.getInt("texelOffset") == 1);    filterData.audioPath = JsonData.getString("audioPath");    filterData.audioLooPing = (JsonData.getInt("audioLooPing") == 1);   }   dynamiccolor.filterList.add(filterData);  }  return dynamiccolor; }

滤镜的实现

在解码得到滤镜参数之后,我们接下来实现动态滤镜渲染过程。为了方便构建滤镜,我们创建一个滤镜资源加载器,代码如下:

/** * 滤镜资源加载器 */public class DynamiccolorLoader { private static final String TAG = "DynamiccolorLoader"; // 滤镜所在的文件夹 private String mFolderPath; // 动态滤镜数据 private DynamiccolorData mcolorData; // 资源索引加载器 private ResourceDataCodec mResourceCodec; // 动态滤镜 private final WeakReference<DynamiccolorBaseFilter> mWeakFilter; // 统一变量列表 private HashMap<String,Integer> mUniformHandleList = new HashMap<>(); // 纹理列表 private int[] mTextureList; // 句柄 private int mTexelWIDthOffsetHandle = Openglutils.GL_NOT_INIT; private int mTexelHeightOffsetHandle = Openglutils.GL_NOT_INIT; private int mStrengthHandle = Openglutils.GL_NOT_INIT; private float mStrength = 1.0f; private float mTexelWIDthOffset = 1.0f; private float mTexelHeightOffset = 1.0f; public DynamiccolorLoader(DynamiccolorBaseFilter filter,DynamiccolorData colorData,String folderPath) {  mWeakFilter = new WeakReference<>(filter);  mFolderPath = folderPath.startsWith("file://") ? folderPath.substring("file://".length()) : folderPath;  mcolorData = colorData;  mStrength = (colorData == null) ? 1.0f : colorData.strength;  Pair pair = ResourceCodec.getResourcefile(mFolderPath);  if (pair != null) {   mResourceCodec = new ResourceDataCodec(mFolderPath + "/" + (String) pair.first,mFolderPath + "/" + pair.second);  }  if (mResourceCodec != null) {   try {    mResourceCodec.init();   } catch (IOException e) {    Log.e(TAG,"DynamiccolorLoader: ",e);    mResourceCodec = null;   }  }  if (!TextUtils.isEmpty(mcolorData.audioPath)) {   if (mWeakFilter.get() != null) {    mWeakFilter.get().setAudioPath(Uri.parse(mFolderPath + "/" + mcolorData.audioPath));    mWeakFilter.get().setLooPing(mcolorData.audioLooPing);   }  }  loadcolorTexture(); } /**  * 加载纹理  */ private voID loadcolorTexture() {  if (mcolorData.uniformDataList == null || mcolorData.uniformDataList.size() <= 0) {   return;  }  mTextureList = new int[mcolorData.uniformDataList.size()];  for (int dataIndex = 0; dataIndex < mcolorData.uniformDataList.size(); dataIndex++) {   Bitmap bitmap = null;   if (mResourceCodec != null) {    bitmap = mResourceCodec.loadBitmap(mcolorData.uniformDataList.get(dataIndex).value);   }   if (bitmap == null) {    bitmap = BitmapUtils.getBitmapFromfile(mFolderPath + "/" + String.format(mcolorData.uniformDataList.get(dataIndex).value));   }   if (bitmap != null) {    mTextureList[dataIndex] = Openglutils.createTexture(bitmap);    bitmap.recycle();   } else {    mTextureList[dataIndex] = Openglutils.GL_NOT_TEXTURE;   }  } } /**  * 绑定统一变量句柄  * @param programHandle  */ public voID onBindUniformHandle(int programHandle) {  if (programHandle == Openglutils.GL_NOT_INIT || mcolorData == null) {   return;  }  mStrengthHandle = GLES30.glGetUniformlocation(programHandle,"strength");  if (mcolorData.texelOffset) {   mTexelWIDthOffsetHandle = GLES30.glGetUniformlocation(programHandle,"texelWIDthOffset");   mTexelHeightOffsetHandle = GLES30.glGetUniformlocation(programHandle,"texelHeightOffset");  } else {   mTexelWIDthOffsetHandle = Openglutils.GL_NOT_INIT;   mTexelHeightOffsetHandle = Openglutils.GL_NOT_INIT;  }  for (int uniformIndex = 0; uniformIndex < mcolorData.uniformList.size(); uniformIndex++) {   String uniformString = mcolorData.uniformList.get(uniformIndex);   int handle = GLES30.glGetUniformlocation(programHandle,uniformString);   mUniformHandleList.put(uniformString,handle);  } } /**  * 输入纹理大小  * @param wIDth  * @param height  */ public voID oninputSizeChange(int wIDth,int height) {  mTexelWIDthOffset = 1.0f / wIDth;  mTexelHeightOffset = 1.0f / height; } /**  * 绑定滤镜纹理,只需要绑定一次就行,不用重复绑定,减少开销  */ public voID onDrawFrameBegin() {  if (mStrengthHandle != Openglutils.GL_NOT_INIT) {   GLES30.gluniform1f(mStrengthHandle,mStrength);  }  if (mTexelWIDthOffsetHandle != Openglutils.GL_NOT_INIT) {   GLES30.gluniform1f(mTexelWIDthOffsetHandle,mTexelWIDthOffset);  }  if (mTexelHeightOffsetHandle != Openglutils.GL_NOT_INIT) {   GLES30.gluniform1f(mTexelHeightOffsetHandle,mTexelHeightOffset);  }  if (mTextureList == null || mcolorData == null) {   return;  }  // 逐个绑定纹理  for (int dataIndex = 0; dataIndex < mcolorData.uniformDataList.size(); dataIndex++) {   for (int uniformIndex = 0; uniformIndex < mUniformHandleList.size(); uniformIndex++) {    // 如果统一变量存在,则直接绑定纹理    Integer handle = mUniformHandleList.get(mcolorData.uniformDataList.get(dataIndex).uniform);    if (handle != null && mTextureList[dataIndex] != Openglutils.GL_NOT_TEXTURE) {     Openglutils.bindTexture(handle,mTextureList[dataIndex],dataIndex + 1);    }   }  } } /**  * 释放资源  */ public voID release() {  if (mTextureList != null && mTextureList.length > 0) {   GLES30.glDeleteTextures(mTextureList.length,mTextureList,0);   mTextureList = null;  }  if (mWeakFilter.get() != null) {   mWeakFilter.clear();  } } /**  * 设置强度  * @param strength  */ public voID setStrength(float strength) {  mStrength = strength; }}

然后我们构建一个DynamiccolorFilter的基类,方便后续添加其他类型的滤镜,代码如下:

public class DynamiccolorBaseFilter extends GlimageAudioFilter { // 颜色滤镜参数 protected DynamiccolorData mDynamiccolorData; protected DynamiccolorLoader mDynamiccolorLoader; public DynamiccolorBaseFilter(Context context,DynamiccolorData dynamiccolorData,String unzipPath) {  super(context,(dynamiccolorData == null || TextUtils.isEmpty(dynamiccolorData.vertexShader)) ? VERTEX_SHADER      : getShaderString(context,unzipPath,dynamiccolorData.vertexShader),(dynamiccolorData == null || TextUtils.isEmpty(dynamiccolorData.fragmentShader)) ? FRAGMENT_SHADER_2D      : getShaderString(context,dynamiccolorData.fragmentShader));  mDynamiccolorData = dynamiccolorData;  mDynamiccolorLoader = new DynamiccolorLoader(this,mDynamiccolorData,unzipPath);  mDynamiccolorLoader.onBindUniformHandle(mProgramHandle); } @OverrIDe public voID oninputSizeChanged(int wIDth,int height) {  super.oninputSizeChanged(wIDth,height);  if (mDynamiccolorLoader != null) {   mDynamiccolorLoader.oninputSizeChange(wIDth,height);  } } @OverrIDe public voID onDrawFrameBegin() {  super.onDrawFrameBegin();  if (mDynamiccolorLoader != null) {   mDynamiccolorLoader.onDrawFrameBegin();  } } @OverrIDe public voID release() {  super.release();  if (mDynamiccolorLoader != null) {   mDynamiccolorLoader.release();  } } /**  * 设置强度,调节滤镜的轻重程度  * @param strength  */ public voID setStrength(float strength) {  if (mDynamiccolorLoader != null) {   mDynamiccolorLoader.setStrength(strength);  } } /**  * 根据解压路径和shader名称读取shader的字符串内容  * @param unzipPath  * @param shadername  * @return  */ protected static String getShaderString(Context context,String unzipPath,String shadername) {  if (TextUtils.isEmpty(unzipPath) || TextUtils.isEmpty(shadername)) {   throw new IllegalArgumentException("shader is empty!");  }  String path = unzipPath + "/" + shadername;  if (path.startsWith("assets://")) {   return Openglutils.getShaderFromAssets(context,path.substring("assets://".length()));  } else if (path.startsWith("file://")) {   return Openglutils.getShaderFromfile(path.substring("file://".length()));  }  return Openglutils.getShaderFromfile(path); }}

接下来我们构建动态滤镜组,因为动态滤镜有可能有多个滤镜组合而成。代码如下:

public class GlimageDynamiccolorFilter extends GlimageGroupFilter { public GlimageDynamiccolorFilter(Context context,Dynamiccolor dynamiccolor) {  super(context);  // 判断数据是否存在  if (dynamiccolor == null || dynamiccolor.filterList == null    || TextUtils.isEmpty(dynamiccolor.unzipPath)) {   return;  }  // 添加滤镜  for (int i = 0; i < dynamiccolor.filterList.size(); i++) {   mFilters.add(new DynamiccolorFilter(context,dynamiccolor.filterList.get(i),dynamiccolor.unzipPath));  } } /**  * 设置滤镜强度  * @param strength  */ public voID setStrength(float strength) {  for (int i = 0; i < mFilters.size(); i++) {   if (mFilters.get(i) != null && mFilters.get(i) instanceof DynamiccolorBaseFilter) {    ((DynamiccolorBaseFilter) mFilters.get(i)).setStrength(strength);   }  } }}

总结

基本的动态滤镜实现起来比较简单,总的来说就是简单的Json参数、shader、统一变量和纹理绑定需要做成动态构建的过程而已。

效果如下:

@H_502_45@


动态滤镜效果

该效果是通过解压asset目录下的压缩包资源来实现的。你只需要提供包含shader 、纹理资源、以及Json的压缩包即可更改滤镜。

详细实现过程,可参考本人的开源项目:
CainCamera

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

总结

以上是内存溢出为你收集整理的Android OpenGLES如何给相机添加滤镜详解全部内容,希望文章能够帮你解决Android OpenGLES如何给相机添加滤镜详解所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1145825.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存