美文网首页OpenGL & Metal
OpenGL ES的滤镜效果 -- 马赛克

OpenGL ES的滤镜效果 -- 马赛克

作者: 黑眼豆豆_ | 来源:发表于2020-08-15 16:58 被阅读0次

想必,对于大家而言,马赛克是我们再熟悉不过的一种效果了。

马赛克是万恶的源头 —— 鲁迅说!

灰度滤镜

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


灰度滤镜.png

灰度滤镜公式

  • 浮点算法: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);
}

正方形马赛克

原理

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

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

代码实现

  • 首先,假定纹理的大小和马赛克的大小马赛克设置的越小,马赛克越密集
//假设纹理大小为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;
}

正六边形马赛克

原理

和正方形马赛克一样,六边形马赛克只是把图片分割成一个一个的六边形,然后取每个六边形的中心点的颜色


正六边形

如图所示,我们取六边形的中心点,然后最终还是可以连接成长方形,而长方形的长宽比为3:√3.
所以假定一个点在屏幕上纹理坐标为(x,y),那么对应的矩阵坐标为

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

所以换算出坐标

坐标换算.png
  • 左上角,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))

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


未命名文件-36.png

判断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种情况,如图


4种情况.png

分别为:

  • 偶行偶列,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;
}

三角形马赛克

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

三角形.png
  • 首先求出纹理坐标点和中心点之间的夹角
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;
}

相关文章

网友评论

    本文标题:OpenGL ES的滤镜效果 -- 马赛克

    本文链接:https://www.haomeiwen.com/subject/gvkadktx.html