OpenGL ES 马赛克滤镜

灰度滤镜效果就是让一张彩色的图片变成灰白的。

灰度滤镜公式

  • 浮点算法:Gray=R*0.3+G*0.59+B*0.11
  • 整数⽅法:Gray=(R*30+G*59+B*11)/100
  • 移位⽅法:Gray =(R*76+G*151+B*28)>>8;
  • 平均值法:Gray=(R+G+B)/3;
  • 仅取绿⾊:Gray=G;

代码实现

  • 我们首先设定一个权重,借鉴了GPUImage,绿色值最高是因为人们对绿色敏感度最高
const highp vec3 W = vec3(0.2125,0.7154,0.0721);

  • 然后计算出纹素中的灰度值,dot指的是点乘
//计算灰度值
float luminance = dot(mask.rgb,W);

  • 最后将灰度值转化为纹素返回给gl_FragColor
gl_FragColor = vec4(vec3(luminance),0.1);

  • 整体片元着色器代码如下:
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),0.1);
}

正方形马赛克

图片选自枫紫

原理

马赛克效果就是把图⽚的⼀个相当⼤⼩的区域⽤同⼀个点的颜⾊来表示,从而隐藏关键信息。

我们可以把图片切割成无数的小的框框,然后再取小框框中某个点的颜色然后再把取出去的颜色整体覆盖到框框中,就会出现一个色块一个色块的效果,就呈现出了马赛克的效果

代码实现

  • 首先,假定纹理的大小和马赛克的大小马赛克设置的越小,马赛克越密集
//假设纹理大小为400.0 * 400.0
const vec2 TextSize = vec2(400.0,400.0);
//设置马赛克大小为10.0 * 10.0
const vec2 MosaicSize = vec2(10.0,10.0);

  • 获取马赛克在假设纹理中的实际坐标
//获取纹理中的实际坐标
vec2 intXY = vec2(TextureCoordsVarying.x * TextSize.x , TextureCoordsVarying.y * TextSize.y);

  • 计算一个小马赛克的坐标
//计算小马赛克的坐标
vec2 XYMosaic = vec2(floor(intXY.x/MosaicSize.x) * MosaicSize.x , floor(intXY.y/MosaicSize.y) * MosaicSize.y);

floor()是glsl的一个内建函数,返回⼩于/等于X的最⼤整数值,即向下取整。

  • 最后换算出在纹理坐标中的位置
//获取纹理坐标中的位置
vec2 UVMosaic = vec2(XYMosaic.x / TextSize.x , XYMosaic.y / TextSize.y);

  • 赋值给gl_FragColor
    vec4 mask = texture2D(Texture, UVMosaic);
    gl_FragColor = mask;

  • 整体代码如下:
precision highp float;
uniform sampler2D Texture;
const vec2 TextSize = vec2(400.0,400.0);
const vec2 MosaicSize = vec2(10.0,10.0);
varying vec2 TextureCoordsVarying;

void main (void) {
    vec2 intXY = vec2(TextureCoordsVarying.x * TextSize.x , TextureCoordsVarying.y * TextSize.y);
    vec2 XYMosaic = vec2(floor(intXY.x/MosaicSize.x) * MosaicSize.x , floor(intXY.y/MosaicSize.y) * MosaicSize.y);
    vec2 UVMosaic = vec2(XYMosaic.x / TextSize.x , XYMosaic.y / TextSize.y);
    vec4 mask = texture2D(Texture, UVMosaic);
    gl_FragColor = mask;
}

正六边形马赛克

原理

image
首先我们设置举行的长宽:画出很多⻓和宽⽐例为 3:√3 的的矩形阵。然后我们可以对每个点进⾏编号,假如我们的屏幕的左上点为上图的(0,0)点,则屏幕上的任⼀点我们找到它所对应的那个矩形了,假定我们设定的矩阵⽐例为 3LEN : √3LEN ,那么屏幕上的任意点(x, y)所对应的矩阵坐标为(int(x/(3LEN)), int(y/(√3LEN)))。wx,wy -> 表⽰纹理坐标在所对应的矩阵坐标为int wx = int(x /( 1.5 * length)); int wy = int(y /(TR * length))。
3:√3的来源:设置矩形的长宽比例值TR、TB(TB:TR 符合比例 3:√3)计算过程如下:

image

获取纹理坐标的x,y,根据纹理坐标计算对应的矩形坐标wx、wy,假设矩阵的比例为3len:√3len,那么纹理坐标(x,y)对应的矩阵坐标为:

image

根据行列的奇偶情况,求对应的中心点纹理坐标v1、v2

偶行偶列:(0,0)(1,1)/,即左上、右下

    偶行奇列:(0,1)(1,0)\,即左下、右上

    奇行偶列:(0,1)(1,0)\,即左下、右上

    奇行奇列:(0,0)(1,1)/,即左上、右下

    最终就两种情况:
image
image
    最终汇总起来也只有2种情况,(0,0)(1,1) 和 (0,1)(1,0),如下图所示
image

其中单个矩阵中,4个点的坐标计算公式如下:

  • 对于计算中的wx+1,拿(1,0)点来说,wx+1等同于(1,0)与(0,0)之间相差一个矩形的长,这个长度为1,为了得到(1,0)点的坐标,要在(0,0)点坐标的基础上,将wx增加一个长

  • 对于计算中的wy+1,拿(0,1)点来说,wy+1等同于(0,0)与(0,1)之间相差一个矩形的高,这个长度为1,为了得到(0,1)点的坐标,要在(0,0)点坐标的基础上,将wy增加一个高

image

最后根据勾股定理,计像素点距离两个中心点的距离s1、s2根据距离公式求像素点距离两个中心点的距离s1、s2

image

作者:枫紫_6174
链接:https://www.jianshu.com/p/e390baf1089b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 //TR = √3/2
 int wx = int(x /( 1.5 * length)); 
 int wy = int(y /(TR * length));

所以换算出坐标

image
  • 左上角,vec2(length * 1.5 * float(wx), length * TR * float(wy))
  • 左下角,vec2(length * 1.5 * float(wx), length * TR * float(wy + 1))
  • 右上角,vec2(length * 1.5 * float(wx + 1), length * TR * float(wy))
  • 右下角,vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1))

所以我们只需要判断纹理对应的点离哪个中心点近即可

image

判断C离A点近还是B点近,我们只需要求出C到A和C到B的距离即可,

//pow是(v1.x - x)的平方
//sqrt是开根号
float s = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));

现在我们要判断4种情况,如图

image

分别为:

  • 偶行偶列,A
//中心点的坐标分别为
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));

  • 偶行奇列,B
//中心点的坐标分别为
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));

  • 奇行偶列,C
//中心点的坐标分别为
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));

  • 奇行奇列,D
//中心点的坐标分别为
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));

最后进行比较,将小的点的坐标当成改区域坐标即可
完整代码如下:

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 TB = 1.5;

    float x = TextureCoordsVarying.x;
    float y = TextureCoordsVarying.y;

    int wx = int(x / TB / 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;
}

三角形马赛克

之前我们已经计算出了正六边形马赛克,根据我们纹理的坐标可以知道究竟在哪个六边形里面,那么三角形就是在六边形的基础上进行细分,将正六边形切割成六块,判断点究竟在哪个三角形里面,如图

image
  • 首先求出纹理坐标点和中心点之间的夹角
float a = atan((x - vn.x)/(y - vn.y));

  • 分别求出六个区域中心点的坐标
    vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TR / 2.0);
    vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
    vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
    vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TR / 2.0);
    vec2 area5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
    vec2 area6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);

  • 判断纹理坐标在哪个区域
    const float PI6 = 0.523599;
    if (a >= PI6 && a < PI6 * 3.0) {
        vn = area1;
    } else if (a >= PI6 * 3.0 && a < PI6 * 5.0) {
        vn = area2;
    } else if ((a >= PI6 * 5.0 && a <= PI6 * 6.0)|| (a<-PI6 * 5.0 && a>-PI6*6.0)) {
        vn = area3;
    } else if (a < -PI6 * 3.0 && a >= -PI6 * 5.0) {
        vn = area4;
    } else if(a <= -PI6 && a> -PI6 * 3.0) {
        vn = area5;
    } else if (a > -PI6 && a < PI6)
    {
        vn = area6;
    }

  • 完整代码如下
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const float mosaicSize = 0.03;

void main (void) {
    float length = mosaicSize;
    const float PI6 = 0.523599;
    float TR = 0.866025;
    float TB = 1.5;

    float x = TextureCoordsVarying.x;
    float y = TextureCoordsVarying.y;

    int wx = int(x / TB / 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 mid = texture2D(Texture, vn);
    float a = atan((x - vn.x)/(y - vn.y));
    vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TR / 2.0);
    vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
    vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
    vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TR / 2.0);
    vec2 area5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
    vec2 area6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);

    if (a >= PI6 && a < PI6 * 3.0) {
        vn = area1;
    } else if (a >= PI6 * 3.0 && a < PI6 * 5.0) {
        vn = area2;
    } else if ((a >= PI6 * 5.0 && a <= PI6 * 6.0)|| (a<-PI6 * 5.0 && a>-PI6*6.0)) {
        vn = area3;
    } else if (a < -PI6 * 3.0 && a >= -PI6 * 5.0) {
        vn = area4;
    } else if(a <= -PI6 && a> -PI6 * 3.0) {
        vn = area5;
    } else if (a > -PI6 && a < PI6)
    {
        vn = area6;
    }

    vec4 color = texture2D(Texture, vn);
    gl_FragColor = color;
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容