OpenGL.Shader:志哥教你写一个滤镜直播客户端(5)视觉滤镜:对比度、曝光、马赛克
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)视觉滤镜:对比度、曝光、马赛克相关推荐
- OpenGL.Shader:志哥教你写一个滤镜直播客户端:仿3个抖音滤镜效果(4镜像/电击/灵魂出窍)
OpenGL.Shader:志哥教你写一个滤镜直播客户端(可能是结束篇) OpenGL.Shader基本的图像处理知识已经学习的7788了,所以这篇应该是滤镜直播客户端的最后一篇了,之后会出基于FFm ...
- 猫哥教你写爬虫 046--协程-实践-吃什么不会胖
吃什么不会胖? 低热量食物 食物的数量有千千万,如果我们要爬取食物热量的话,这个数据量必然很大. 使用多协程来爬取大量的数据是非常合理且明智的选择 如果我们要爬取的话,那就得选定一个有存储食物热量信息 ...
- 猫哥教你写爬虫 006--条件判断和条件嵌套
流程控制 复仇者联盟3-无限战争(搜集宝石) python里面, 不需要使用;来结尾, 因为python是使用换行来结束一行代码的 if判断, 没有{}, python使用缩进来表示层级关系 if.. ...
- 猫哥教你写爬虫 037--爬虫-宝宝要听歌
戴上耳机, 这个世界与我无关... 让我们用音乐洗涤心灵吧... 我们从哪个网站爬取资源呢? 专治各种不服... 打开酷狗官网, 可以看到搜索框,我们要爬取的数据就是搜索歌曲后, 酷狗后台返回的歌曲列 ...
- 手把手教你写一个生成对抗网络
成对抗网络代码全解析, 详细代码解析(TensorFlow, numpy, matplotlib, scipy) 那么,什么是 GANs? 用 Ian Goodfellow 自己的话来说: " ...
- python k线合成_手把手教你写一个Python版的K线合成函数
手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...
- 猫哥教你写爬虫 002--作业-打印皮卡丘
作业 请你使用print()函数将下面的皮卡丘打印出来, 使用三种方式 へ /|/\7 ∠_// │ / /│ Z _,< / /`ヽ│ ヽ / 〉Y ` / /イ● 、 ● ⊂⊃〈 /() へ ...
- cmd管道无法接收特定程序返回值_渗透不会反弹shell?来教你写一个cmd的shell
渗透不会反弹shell?来教你写一个cmd的shell 包含的库: #include #include #include #include #include #pragma comment(lib, ...
- 猫哥教你写爬虫 005--数据类型转换-小作业
小作业 程序员的一人饮酒醉 请运用所给变量,使用**str()**函数打印两句话. 第一句话:1人我编程累, 碎掉的节操满地堆 第二句话:2眼是bug相随, 我只求今日能早归 number1 = 1 ...
最新文章
- 宝塔控制面板创建ftp后链接不上的解决方法
- 用sort()方法随机打乱数组
- 【Daily Scrum】12-08
- arcx函数js_抖音的这个JS,怎么能运行,获取到用户的加载信息
- Windows说明Linux分区和挂载点
- android 串口工具下载,SerialTool串口调试工具-SerialTool apkv1.2 android最新版_永辉资源网...
- Promise对象和运算符
- java mapxtreme_MapXtreme Java Edition 4.8使用心得(一)
- 安装flash cs6失败解决方案
- 关于H5的标签整理合集(一)
- 使用accton进行进程会计处理
- 【早期笔记】java 开发环境搭建
- java实现捕鱼达人
- 车载智能导航系统有没有采用嵌入式计算机,嵌入式车载导航系统的应用与研究...
- Linux_Learning_兄弟连
- 大厂代码规范及个人本学期的代码规范
- 《日语综合教程》第七册 第六課 自然と人間
- 工作中遇到的问题之android客户端自动生成带logo的二维码
- 设计思维引导设计实践
- 有限体积法(12)——SIMPLE算法