美文网首页
OpenGL ES 马赛克滤镜

OpenGL ES 马赛克滤镜

作者: 大橘猪猪侠 | 来源:发表于2020-08-11 22:09 被阅读0次

今天我们来讨论一下图片的马赛克是如何生成的。

在之前我们使用OpenGL ES实现了图片的滤镜现实,利用自定义的顶点着色器和片元着色器去实现。那么在实现分屏滤镜时,我们修改的是片元着色器,通过修改纹理坐标显示的内容来实现的。那么在对图片添加马赛克的原理其实也是修改纹理坐标范围内显示的内容区域大小,通俗来讲,就是把图片的一个相当大小的区域用同一个点的颜色来表示,可以认为是大规模的降低图像的分辨率,让图片的一些细节的地方隐藏起来。

下面我们来详细的介绍如何修改片元着色器代码来实现图片的马赛克。

本文中我们显示的马赛克形状主要分为正方形,正六边形和三角形。首先从最容易实现的正方形开始讨论。

正方形马赛克:

实现正方形马赛克主要是将固定区域内的颜色显示同一个颜色;因此,我们需要定义一个二维变量去表示正方形区域大小:vec2 mosaicSize;其次,我们需要得到有多少个方块,那么,就要知道滤镜图片的大小,那么我们就要定义一个图片的大小,通过与真实纹理坐标的比例去实现效果;最后赋值。
片元着色器代码:

precision highp float;
//纹理坐标
uniform sampler2D Texture;
//纹理采样器
varying vec2 TextureCoordsVarying;
//纹理图片大小
const vec2 TexSize = vec2(400.0,400.0);
//马赛克大小
const vec2 mosaicSize = vec2(10.0,10.0);

void main(void){
//计算图像实际位置
    vec2 intXY = vec2(TextureCoordsVarying.x*TexSize.x,TextureCoordsVarying.y*TexSize.y);
    //计算一个小马赛克坐标
    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;
}

正方形马赛克最终效果:


Simulator Screen Shot - iPhone SE -2nd generation- - 2020-08-11 at 21.39.08.png

正六边形马赛克:
正六边形马赛克处理比较复杂,下面通过图像来显示。

15971531913842.png

看上面的图形,每一个黄色的点为一个纹理坐标,每四个点之间存在的情况有两种。


截屏2020-08-11 下午9.41.43.png 截屏2020-08-11 下午9.41.48.png

看上面两个小方块的图形(因为是截取的,很模糊,但是不影响我们观看),我们从已知的结论中可以知道,矩形的长宽比为3:3^0.5。我们可以对每个点进行编号。我们设定举行比例为 3LEN : √3LEN**;那么任意一点(x,y)多对应举行坐标为:

(int(x/(3*LEN)), int(y/ (√3*LEN)))

纹理坐标对应矩阵坐标为:

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

接下来,我们对矩形坐标用数据表示出来:

15971534942094.png

左上顶点:vec2(length * 1.5 *float(wx),length * 0.866025 *float(wy))

右上顶点:vec2(length * 1.5 * float(wx + 1), length * 0.866025 * float(wy))

左下顶点:vec2(length * 1.5 * float(wx), length * 0.866025 * float(wy + 1));

右下顶点:vec2(length * 1.5 * float(wx + 1), length * 0.866025 * float(wy + 1));

在顶点坐标换算完之后,你会发现一个问题,就是每个矩形框的斜线位置是不一样的,但是总的来看,只有两种情况一个是:左上/右下,另一个是:左下/右上。

那么我们如何辨别呢?
通过纹理坐标的奇偶行,一共分8种情况,具体看下面的代码。

最后一步,我们如何判断某一点的坐标到底应该是现实哪一个点的颜色呢?
答案是通过与两个顶点的距离,具体看下面代码:

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){
            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{
            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){
            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{
            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;
}

六边形马赛克效果:

Simulator Screen Shot - iPhone SE -2nd generation- - 2020-08-11 at 22.06.59.png

接下来实现三角形马赛克:
三角形马赛克是在六边形马赛克的基础上扩展出来的,通过对六边形划分6个区域,每个区域都为一个正三角形。然后通过判断纹理坐标点的角度,去判断该点在哪个区域,最后将该区域的颜色赋值给vn。

不同代码:

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;
    }

三角形马赛克效果:


Simulator Screen Shot - iPhone SE -2nd generation- - 2020-08-11 at 22.07.24.png

完整代码:demo

相关文章

网友评论

      本文标题:OpenGL ES 马赛克滤镜

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