OpenGL.Shader:志哥教你写一个滤镜直播客户端(5)

上一章介绍了如何在渲染nv21流的时候进行滤镜的无缝切换,这章内容紧接上一章,介绍三种滤镜特效:对比度、曝光、马赛克,并介绍如何动态调节滤镜效果。废话不说,show the code!

滤镜1:对比度

#include "GpuBaseFilter.hpp"
/*** 更改图像的对比度。* 对比度值在0.0到4.0之间,正常值为1.0*/
class GpuContrastFilter : public GpuBaseFilter {
public:int getTypeId() { return FILTER_TYPE_CONTRAST; }GpuContrastFilter(){CONTRAST_FRAGMENT_SHADER  ="precision mediump float;\n\varying highp vec2 textureCoordinate;\n\uniform sampler2D SamplerRGB;\n\uniform sampler2D SamplerY;\n\uniform sampler2D SamplerU;\n\uniform sampler2D SamplerV;\n\uniform lowp float contrast;\n\mat3 colorConversionMatrix = mat3(\n\1.0, 1.0, 1.0,\n\0.0, -0.39465, 2.03211,\n\1.13983, -0.58060, 0.0);\n\vec3 yuv2rgb(vec2 pos)\n\{\n\vec3 yuv;\n\yuv.x = texture2D(SamplerY, pos).r;\n\yuv.y = texture2D(SamplerU, pos).r - 0.5;\n\yuv.z = texture2D(SamplerV, pos).r - 0.5;\n\return colorConversionMatrix * yuv;\n\}\n\void main()\n\{\n\vec4 textureColor = vec4(yuv2rgb(textureCoordinate), 1.0);\n\gl_FragColor = vec4((contrast*(textureColor.rgb - vec3(0.5)) + vec3(0.5)), textureColor.w);\n\}";}~GpuContrastFilter() {if(!CONTRAST_FRAGMENT_SHADER.empty()) CONTRAST_FRAGMENT_SHADER.clear();}void init() {GpuBaseFilter::init(NO_FILTER_VERTEX_SHADER.c_str(), CONTRAST_FRAGMENT_SHADER.c_str());mContrastLocation = glGetUniformLocation(mGLProgId, "contrast");mContrastValue = 1.0f;}void setAdjustEffect(float percent) {mContrastValue = percent * 4.0f;} // (经验范围控制在0~4)void onDraw(GLuint SamplerY_texId, GLuint SamplerU_texId, GLuint SamplerV_texId,void* positionCords, void* textureCords){ // ...}private:std::string CONTRAST_FRAGMENT_SHADER;GLint   mContrastLocation;float   mContrastValue;
};

顶点着色器沿用基类GpuBaseFilter.NO_FILTER_VERTEX_SHADER,分析一波片元着色器,不难发现对比度原理:

gl_FragColor = vec4((contrast*(textureColor.rgb - vec3(0.5)) + vec3(0.5)), textureColor.w);

textureColor.rgb - vec3(0.5)是用于二分对齐,也可以理解为是量化;contrast就是对比度因子;乘以对比度用以扩大不同色值的层级范围。(经验范围控制在0~4)

显然这个contrast是要能动态进行调节的,回想上一章内容的设计方法,在GpuFilterRender跟踪setAdjustEffect(float percent)不难发现也是在renderOnDraw调用,部分代码如下:

void GpuFilterRender::renderOnDraw(double elpasedInMilliSec)
{// 画面渲染mWindowSurface->makeCurrent();yTextureId = updateTexture(dst_y, yTextureId, mFrameWidth, mFrameHeight);uTextureId = updateTexture(dst_u, uTextureId, mFrameWidth/2, mFrameHeight/2);vTextureId = updateTexture(dst_v, vTextureId, mFrameWidth/2, mFrameHeight/2);// 检测更新FiltercheckFilterChange();if( mFilter!=NULL) {mFilter->setAdjustEffect(mFilterEffectPercent);mFilter->onDraw(yTextureId, uTextureId, vTextureId, positionCords, textureCords);}// ...
}
void GpuFilterRender::adjustFilterValue(int value, int max) {mFilterEffectPercent = (float)value / (float)max;//LOGD("GpuFilterRender adjust %f", mFilterEffectPercent);
}
///gpu_filter_jni//
JNIEXPORT void JNICALL
Java_org_zzrblog_gpufilter_GpuFilterRender_adjustFilterValue(JNIEnv *env, jobject instance, jint value, jint max) {if (render == NULL)render = new GpuFilterRender();render->adjustFilterValue(value, max);
}

继续回溯跟踪,可以发现通过Activity调用Seekbar,然后再调用CfeScheduler.adjustFilterValue(value, max),最后实现动态调节对比度因子contrast,效果如下:

滤镜2:曝光(黑白反转)

#include "GpuBaseFilter.hpp"
/*** 反转图像中的所有颜色。*/
class GpuColorInvertFilter : public GpuBaseFilter {
public:int getTypeId() { return FILTER_TYPE_COLOR_INVERT; }GpuColorInvertFilter(){COLOR_INVERT_FRAGMENT_SHADER="precision mediump float;\n\varying highp vec2 textureCoordinate;\n\uniform sampler2D SamplerRGB;\n\uniform sampler2D SamplerY;\n\uniform sampler2D SamplerU;\n\uniform sampler2D SamplerV;\n\mat3 colorConversionMatrix = mat3(\n\1.0, 1.0, 1.0,\n\0.0, -0.39465, 2.03211,\n\1.13983, -0.58060, 0.0);\n\vec3 yuv2rgb(vec2 pos)\n\{\n\vec3 yuv;\n\yuv.x = texture2D(SamplerY, pos).r;\n\yuv.y = texture2D(SamplerU, pos).r - 0.5;\n\yuv.z = texture2D(SamplerV, pos).r - 0.5;\n\return colorConversionMatrix * yuv;\n\}\n\void main()\n\{\n\vec4 textureColor = vec4(yuv2rgb(textureCoordinate), 1.0);\n\gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);\n\}";}~GpuColorInvertFilter() {if(!COLOR_INVERT_FRAGMENT_SHADER.empty()) COLOR_INVERT_FRAGMENT_SHADER.clear();}void init() {GpuBaseFilter::init(NO_FILTER_VERTEX_SHADER.c_str(), COLOR_INVERT_FRAGMENT_SHADER.c_str());}private:std::string COLOR_INVERT_FRAGMENT_SHADER;
};

顶点着色器还是沿用基类GpuBaseFilter.NO_FILTER_VERTEX_SHADER,分析一波片元着色器,不难发现曝光的原理:

gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w); 

其实就是取反啊!而且还不需要动态调节,so easy (个_个)

上个效果图意思意思:

滤镜3:马赛克

最后一个也是最有意思的一个滤镜效果,各位老司机可能就是最讨厌的一个了(斜眼笑.jpg)

#include "GpuBaseFilter.hpp"
/*** 对图像应用格仔化效果。*/
class GpuPixelationFilter : public GpuBaseFilter {
public:int getTypeId() { return FILTER_TYPE_PIXELATION; }GpuPixelationFilter(){PIXELATION_FRAGMENT_SHADER="precision highp float;\n\varying highp vec2 textureCoordinate;\n\uniform sampler2D SamplerRGB;\n\uniform sampler2D SamplerY;\n\uniform sampler2D SamplerU;\n\uniform sampler2D SamplerV;\n\mat3 colorConversionMatrix = mat3(\n\1.0, 1.0, 1.0,\n\0.0, -0.39465, 2.03211,\n\1.13983, -0.58060, 0.0);\n\uniform float imageWidthFactor;\n\uniform float imageHeightFactor;\n\uniform float pixel;\n\vec3 yuv2rgb(vec2 pos)\n\{\n\vec3 yuv;\n\yuv.x = texture2D(SamplerY, pos).r;\n\yuv.y = texture2D(SamplerU, pos).r-0.5;\n\yuv.z = texture2D(SamplerV, pos).r-0.5;\n\return colorConversionMatrix * yuv;\n\}\n\void main()\n\{\n\vec2 uv  = textureCoordinate.xy;\n\float dx = pixel * imageWidthFactor;\n\float dy = pixel * imageHeightFactor;\n\vec2 coord = vec2(dx*floor(uv.x / dx), dy*floor(uv.y / dy));\n\gl_FragColor = vec4(yuv2rgb(coord), 1.0);\n\}";}~GpuPixelationFilter() {if(!PIXELATION_FRAGMENT_SHADER.empty()) PIXELATION_FRAGMENT_SHADER.clear();}void init() {GpuBaseFilter::init(NO_FILTER_VERTEX_SHADER.c_str(), PIXELATION_FRAGMENT_SHADER.c_str());mPixelLocation = glGetUniformLocation(mGLProgId, "pixel");mImageWidthFactorLocation = glGetUniformLocation(mGLProgId, "imageWidthFactor");mImageHeightFactorLocation = glGetUniformLocation(mGLProgId, "imageHeightFactor");mPixelValue = 1.0f;}void setAdjustEffect(float percent) {if(percent==0.0f) percent=0.01f;mPixelValue = percent * 100.0f;}void onOutputSizeChanged(int width, int height) {GpuBaseFilter::onOutputSizeChanged(width, height);glUniform1f(mImageWidthFactorLocation, 1.0f / width);glUniform1f(mImageHeightFactorLocation, 1.0f / height);}// ...void onDraw(GLuint SamplerY_texId, GLuint SamplerU_texId, GLuint SamplerV_texId,void* positionCords, void* textureCords){if (!mIsInitialized)return;glUseProgram(mGLProgId);glUniform1f(mPixelLocation, mPixelValue);glUniform1f(mImageWidthFactorLocation, 1.0f / mOutputWidth);glUniform1f(mImageHeightFactorLocation, 1.0f / mOutputHeight);// 绘制的模板代码,此处省略}
};

内容有点多,一起来分析一波:

uniform float imageWidthFactor; // 当前屏幕宽度因子,取值为当前宽度的1/10
uniform float imageHeightFactor;
uniform float pixel; // 采样跨度void main()
{vec2 uv  = textureCoordinate.xy; // 当前纹理坐标float dx = pixel * imageWidthFactor;  // 根据采样跨度,调整步长float dy = pixel * imageHeightFactor;// floor(uv.x / dx)“向下舍入”,具体数值说明白,屏幕720*1280,widthFacetor=72// uv.x = 1,pixel = 1,代入计算 72*(floor(1/72)) = 0// uv.x = 2,pixel = 1,代入计算 72*(floor(2/72)) = 0// uv.x = 71,pixel = 1,代入计算 72*(floor(71/72)) = 0// uv.x = 72,pixel = 1,代入计算 72*(floor(72/72)) = 72//以上可以说明,通过计算,可以把步长范围内的所有纹理坐标,锁定到范围内的第一个像素坐标位置进行纹理采样。vec2 coord = vec2(dx*floor(uv.x / dx), dy*floor(uv.y / dy));gl_FragColor = vec4(yuv2rgb(coord), 1.0);
}";

分析完毕,放效果图看看。

       

项目地址: https://github.com/MrZhaozhirong/NativeCppApp    shader集中放置在src\main\cpp\gpufilter\filter

OpenGL.Shader:志哥教你写一个滤镜直播客户端(5)视觉滤镜:对比度、曝光、马赛克相关推荐

  1. OpenGL.Shader:志哥教你写一个滤镜直播客户端:仿3个抖音滤镜效果(4镜像/电击/灵魂出窍)

    OpenGL.Shader:志哥教你写一个滤镜直播客户端(可能是结束篇) OpenGL.Shader基本的图像处理知识已经学习的7788了,所以这篇应该是滤镜直播客户端的最后一篇了,之后会出基于FFm ...

  2. 猫哥教你写爬虫 046--协程-实践-吃什么不会胖

    吃什么不会胖? 低热量食物 食物的数量有千千万,如果我们要爬取食物热量的话,这个数据量必然很大. 使用多协程来爬取大量的数据是非常合理且明智的选择 如果我们要爬取的话,那就得选定一个有存储食物热量信息 ...

  3. 猫哥教你写爬虫 006--条件判断和条件嵌套

    流程控制 复仇者联盟3-无限战争(搜集宝石) python里面, 不需要使用;来结尾, 因为python是使用换行来结束一行代码的 if判断, 没有{}, python使用缩进来表示层级关系 if.. ...

  4. 猫哥教你写爬虫 037--爬虫-宝宝要听歌

    戴上耳机, 这个世界与我无关... 让我们用音乐洗涤心灵吧... 我们从哪个网站爬取资源呢? 专治各种不服... 打开酷狗官网, 可以看到搜索框,我们要爬取的数据就是搜索歌曲后, 酷狗后台返回的歌曲列 ...

  5. 手把手教你写一个生成对抗网络

    成对抗网络代码全解析, 详细代码解析(TensorFlow, numpy, matplotlib, scipy) 那么,什么是 GANs? 用 Ian Goodfellow 自己的话来说: " ...

  6. python k线合成_手把手教你写一个Python版的K线合成函数

    手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...

  7. 猫哥教你写爬虫 002--作业-打印皮卡丘

    作业 请你使用print()函数将下面的皮卡丘打印出来, 使用三种方式 へ /|/\7 ∠_// │ / /│ Z _,< / /`ヽ│ ヽ / 〉Y ` / /イ● 、 ● ⊂⊃〈 /() へ ...

  8. cmd管道无法接收特定程序返回值_渗透不会反弹shell?来教你写一个cmd的shell

    渗透不会反弹shell?来教你写一个cmd的shell 包含的库: #include #include #include #include #include #pragma comment(lib, ...

  9. 猫哥教你写爬虫 005--数据类型转换-小作业

    小作业 程序员的一人饮酒醉 请运用所给变量,使用**str()**函数打印两句话. 第一句话:1人我编程累, 碎掉的节操满地堆 第二句话:2眼是bug相随, 我只求今日能早归 number1 = 1 ...

最新文章

  1. 宝塔控制面板创建ftp后链接不上的解决方法
  2. 用sort()方法随机打乱数组
  3. 【Daily Scrum】12-08
  4. arcx函数js_抖音的这个JS,怎么能运行,获取到用户的加载信息
  5. Windows说明Linux分区和挂载点
  6. android 串口工具下载,SerialTool串口调试工具-SerialTool apkv1.2 android最新版_永辉资源网...
  7. Promise对象和运算符
  8. java mapxtreme_MapXtreme Java Edition 4.8使用心得(一)
  9. 安装flash cs6失败解决方案
  10. 关于H5的标签整理合集(一)
  11. 使用accton进行进程会计处理
  12. 【早期笔记】java 开发环境搭建
  13. java实现捕鱼达人
  14. 车载智能导航系统有没有采用嵌入式计算机,嵌入式车载导航系统的应用与研究...
  15. Linux_Learning_兄弟连
  16. 大厂代码规范及个人本学期的代码规范
  17. 《日语综合教程》第七册 第六課 自然と人間
  18. 工作中遇到的问题之android客户端自动生成带logo的二维码
  19. 设计思维引导设计实践
  20. 有限体积法(12)——SIMPLE算法

热门文章

  1. POJ - 1080 Human Gene Functions解题报告(求最长相同子序列)
  2. chrome://inspect/#devices敲定手机chromedriver的版本
  3. 【个人博客 hexo】一个小时就搭好属于自己的博客
  4. 计算机基础 键盘认识,计算机基础-认识键盘
  5. 关于烧录的hex文件
  6. samba服务器--samba简介(一)
  7. Spark SQL编程指南-收费版
  8. vivado 仿真工程中$readmemh 使用
  9. 纽约时报夏季读写竞赛复盘
  10. 小米手机关机了闹钟还会响吗_教你设置小米手机关机闹钟铃声