音视频开发 - 灰度/颠倒/旋涡/正方形马赛克/六边形马赛克滤镜

灰度滤镜

灰度滤镜就是获取我们YUV颜色空间里的亮度值Y,我们现在只讲解FragmentShader改动的部分,其他的顶点着色器中的代码是不用改动的。
灰度滤镜用很多中设置方法:

1、仅取绿色值法 - 将三种RGB色值都设置为G分量的值

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);

void main (void) {
    
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    gl_FragColor = vec4(mask.g,mask.g,mask.g,1.0);
}

其中mask纹理像素值中的RGB全部设置为了mask中的G分量值。
绿色值法灰度

2、浮点算法(权值法) - 对其中的颜色分量设置权重,我们设置的权重是按照GPUImage中的权重进行设置的(0.2125, 0.7154, 0.0721),看代码

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);

void main (void) {
    
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    float luminance = dot(mask.rgb, W);
    gl_FragColor = vec4(vec3(luminance), 1.0);
}

按照我们设置的权重值,分别为rgb中的分量设置权重,比较容易理解
浮点算法

这两个滤镜方法进行比较,其实仔细观察还是能看出下边的浮点算法滤镜的灰度会更纯好一些,上边的图片还是有一些其他颜色

颠倒滤镜

颠倒滤镜其实就是讲图片进行上下/左右的反转,这里我们以上下反转作为实例,直接看片元着色器部分的代码

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

void main (void) {
    
    vec4 color = texture2D(Texture, vec2(TextureCoordsVarying.x, 1.0 - TextureCoordsVarying.y));
    
    gl_FragColor = color;
}

其实只是将y方向的分量进行了反转,即用1-实际的纹理坐标中的y值,就得到了上下反转的结果,还是比较简单的
颠倒滤镜

旋涡滤镜

旋涡滤镜其实就是将照片中间部分纹理进行一个一个旋转,并且随着半径增大旋转角度也会增大,直接看片元着色器中的代码部分

precision mediump float;

const float PI = 3.14159265;
uniform sampler2D Texture;

const float uD = 80.0;
const float uR = 0.5;

varying vec2 TextureCoordsVarying;

void main()
{
    ivec2 ires = ivec2(512, 512);
    float Res = float(ires.s);
    
    vec2 st = TextureCoordsVarying;
    float Radius = Res * uR;
    
    vec2 xy = Res * st;
    
    vec2 dxy = xy - vec2(Res/2., Res/2.);
    float r = length(dxy);
    
    //(1.0 - r/Radius);
    float beta = atan(dxy.y, dxy.x) + radians(uD) * 2.0 * (-(r/Radius)*(r/Radius) + 1.0);
    
    vec2 xy1 = xy;
    if(r<=Radius)
    {
        xy1 = Res/2. + r*vec2(cos(beta), sin(beta));
    }
    
    st = xy1/Res;
    
    vec3 irgb = texture2D(Texture, st).rgb;
    
    gl_FragColor = vec4( irgb, 1.0 );
}

PI:我们的计算中的π,取值3.14159265
uR:设置为0.5,其实就是为了取旋涡半径用到的
ivec2:整形的二维向量,这里的ires其实就是一个512宽高的正方形
Res:取出当前正方形的边长
uD:旋转的角度,Radius是我们旋转的半径
xy:通过直径获得纹理坐标对应的物体坐标
dxy:取出纹理坐标减去半径之后的具体物体坐标
r:当前半径

最重要的旋转角度如何设置?

float beta = atan(dxy.y, dxy.x) + radians(uD) * 2.0 * (-(r/Radius)*(r/Radius) + 1.0);

atan(dxy.y, dxy.x):获取的当前的夹角,如果不设置其他的值,那么当前图片没有任何旋转效果。
radians(uD) * 2.0:在原来夹角的基础上加上我们设置的旋转角度802 = 160
(-(r/Radius)
(r/Radius) + 1.0):抛物线衰减因子,通过距离圆心的距离计算我们旋转衰减的增益值

假如我们的向量的模r小于当前的正方形的一半也就是当前圆的半径:
r*vec2(cos(beta), sin(beta)) :旋涡效果
Res/2.:半径
得出旋转后的坐标
st = xy1/Res - 计算旋转后的纹理坐标

最终得到我们的实际显示颜色
旋涡滤镜

正方形马赛克

正方形马赛克就是马赛克的形状是正方形的
看代码部分

precision mediump float;

varying vec2 TextureCoordsVarying;
uniform sampler2D Texture;
//纹理图像尺寸
const vec2 TexSize = vec2(400.0, 400.0);
//马赛克设置的实际尺寸
const vec2 mosaicSize = vec2(10.0, 10.0);

void main()
{
    //实际图像位置
    vec2 intXY = vec2(TextureCoordsVarying.x*TexSize.x, TextureCoordsVarying.y*TexSize.y);
    //floor返回小于、等于x的最大整数
    //通过出发公式可以获取到某一段以内的数值都设置为同一个值
    vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);
    vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize.y);
    vec4 color = texture2D(Texture, UVMosaic);
    gl_FragColor = color;
}

其中下边的方法是关键,如果在某一区间内,将这一段区域的像素值都设置为某一个整数位置的像素值,达到正方形马赛克的效果

 vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);

六边形马赛克

六边形的马赛克就稍微有点复杂了,我们需要知道六边形的几何知识然后在解释,让自己更加理解.

我们将一张图片分割成六边形,并且让每一个六边形的颜色相同(取六边型的中心点作为我们整个六边形的颜色值),将其分割,分割之后其实有四种不同的矩形将一个六边形分割开来。

六边形和其像素点的选择

我们对照代码来分析:

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

//六边型的增量
const float mosaicSize = 0.03;

void main (void)
{
    float length = mosaicSize;
    float TR = 0.866025;
    
    //纹理坐标值
    float x = TextureCoordsVarying.x;
    float y = TextureCoordsVarying.y;
    
    //转化为矩阵中的坐标
    int wx = int(x / 1.5 / length);
    int wy = int(y / TR / length);
    vec2 v1, v2, vn;
    
    //分析矩阵中的坐标是在奇数还是在偶数行,根据奇数偶数值来确定我们的矩阵的角标坐标值
    if (wx/2 * 2 == wx) {
        if (wy/2 * 2 == wy) {
            //(0,0),(1,1)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
        } else {
            //(0,1),(1,0)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
        }
    }else {
        if (wy/2 * 2 == wy) {
            //(0,1),(1,0)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
        } else {
            //(0,0),(1,1)
            v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
            v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
        }
    }
    
    //获取距离
    float s1 = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));
    float s2 = sqrt(pow(v2.x - x, 2.0) + pow(v2.y - y, 2.0));
    
    //设置具体的纹理坐标
    if (s1 < s2) {
        vn = v1;
    } else {
        vn = v2;
    }
    vec4 color = texture2D(Texture, vn);
    
    gl_FragColor = color;
    
}

首先我们应该知道一个六边型的宽高比是3:√3,所以我们设置组成六边型的矩形比例也是3:√3,然后乘以一个增量mosaicSize = 0.03,然后我们将原纹理像素坐标转化到我们的矩形中得到矩形的坐标值

 int wx = int(x / 1.5 / length);
 int wy = int(y / TR / length);

然后先看一个纹理元素的坐标
纹理坐标

纹理坐标对应到我们六边形中组成的矩形时,通过上边矩阵的坐标值反推得到原始纹理坐标值,然后对应纹理坐标系中的变换得到矩阵纹理坐标点
矩形定点的纹理坐标

接下来,我们分析整个由六边形组成的图中,矩阵的坐标到底应该参考哪一个点
由最上边的图中可以知道,当当前位置在
横轴是偶数,纵轴是奇数的向量位置取左上角右下角
横轴是偶数,纵轴是偶数的向量位置取左下角右上角
横轴是奇数,纵轴是奇数的向量位置取左上角右下角
横轴是奇数,纵轴是偶数的向量位置取左下角右上角

所以才有了上边的判断坐标的代码

最后根据距离这两个角的距离,确定应该采用哪一个纹理坐标获取当前的纹理像素值,看效果如下
六边形马赛克

欢迎关注我的公众号,专注iOS开发、大前端开发、跨平台技术分享。


iOS开发之家
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 前情提要 这篇滤镜效果的实现是在上一篇分屏滤镜的基础上来进行实现的,同样的前提是可以利用GLSL加载一张正常的图片...
    Henry_Jeannie阅读 942评论 0 3
  • 一、灰度滤镜 灰度滤镜的几种算法 浮点算法: Gray = R * 0.3 + G * 0.59 + B * 0....
    SPIREJ阅读 1,065评论 0 2
  • 如果你有一个十多岁的宝贝女儿,你一定思量过:她的未来将与什么样的人相依为命。你会说,这是不是太荒唐了,她才十岁耶?...
    阿萍28阅读 535评论 0 0
  • 【0801今日剽悍】7425-溪山 【目标】01交寇伟政审谈话记录;02葡萄测产数据整理;03得到APP学习;04...
    溪山随笔阅读 96评论 0 0
  • 关键在于,我们是否在一直专注做一件事。如果我们能有长期的,专注于做一件事情,在其中,不断的成长自我。从技能到思想全...
    春风十里木有一路桃花阅读 166评论 0 1