OpenGL ES --颠倒滤镜、灰度滤镜以及三种马赛克效果的实现原理探究

本篇文章的基础代码OpenGL ES --使用GLSL语言加载一张图,关于滤镜效果的实现,主要是实现片元着色器的算法,所以相关效果的顶点着色器是一样的不需要改动。

效果图

一、颠倒滤镜的实现原理
  • 原理:将纹理坐标的Y值进行一个颠倒操作,1- 原来的纹理坐标y值
  • 效果图
    颠倒滤镜效果图
  • 颠倒滤镜片元着色器代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
void main (void) {

    vec4 mask = texture2D(Texture, vec2(TextureCoordsVarying.x,1.0-TextureCoordsVarying.y));
    
    gl_FragColor = vec4(mask.rgb, 1.0);
}

二、灰度滤镜的实现原理
  • 原理:灰度滤镜主要是让RGB值保持一个平衡并填充,或者只保留一个亮度值,当只保留一个亮度值的时候,我们仅仅使用绿色,因为绿色是人眼的生理现象,让看到的图片更暗淡。
  • 关于滤镜算法主要有五种方式
    1.浮点算法:Gray=R*0.3+G*0.59+B*0.11
    2.整数⽅法:Gray=(R*30+G*59+B*11)/100
    3.移位⽅法:Gray =(R*76+G*151+B*28)>>8;
    4.平均值法:Gray=(R+G+B)/3;
    5.仅取绿⾊:Gray=G
  • 效果图
    灰度滤镜效果图
  • 灰度滤镜片元着色器代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

//RGB的变换因子,即权重值,该权重值参考GPUImage的值
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);

void main(){
    //获取对应纹理坐标系下色颜色值
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    
    //将颜色mask 与 变换因子相乘得到灰度值
    float luminance = dot(mask.rgb, W);
    
    //将灰度值转换为(luminance,luminance,luminance,mask.a)并填充到像素中
    gl_FragColor = vec4(vec3(luminance), 1.0);
}

思考:什么是马赛克效果:⻢赛克效果就是把图⽚的⼀个相当⼤⼩的区域⽤同⼀个点的颜⾊来表示.可以认为是⼤规模的降低图像的分辨率,⽽让图像的⼀些细节隐藏起来。

三、正方形马赛克的实现原理
  • 原理:
    第一步:根据纹理坐标来计算实际图像的位置;
    第二步:根据内建函数floor (x) 内建函数,返回⼩于/等于X的最⼤整数值,来获取一个小正方形马赛克的坐标;
    第三步:在由纹理图片的大小换算出纹理坐标;
    第四步:根据马赛克纹理坐标获取到该纹理坐标的颜色值
  • 实现效果


    正方形马赛克效果图
  • 正方形滤镜片元着色器实现代码
precision mediump float;
//纹理坐标
varying vec2 TextureCoordsVarying;
//纹理采样器
uniform sampler2D Texture;
//纹理图⽚size
const vec2 TexSize = vec2(400.0, 400.0);
//⻢赛克Size
const vec2 mosaicSize = vec2(16.0, 16.0);
void main()
{
 //计算实际图像位置 
 vec2 intXY = vec2(TextureCoordsVarying.x*TexSize.x, TextureCoordsVarying.y*TexSize.y);
 // floor (x) 内建函数,返回⼩于/等于X的最⼤整数值.
 // floor (intXY.x / mosaicSize.x) * mosaicSize.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.
 gl_FragColor = color;
}
四、六边形马赛克的实现原理
  • 原理:我们要做的效果就是让⼀张图⽚,分割成由六边形组成,让每个六边形中的颜⾊相同(直接取六边形中⼼点像素RGB较⽅便,我们这⾥采⽤的就是这种⽅法),将它进行分割,取每个六边形的中心画出一个矩阵
    截屏2020-08-12 16.09.32.png
    如上图,画出很多⻓和宽⽐例为 3:√3 的的矩形阵。然后我们可以对每个点进⾏编号,如上图中,采⽤坐标系标记.假如我们的屏幕的左上点为上图的(0,0)点,则屏幕上的任⼀点我们找到它所对应的那个矩形了。假定我们设定的矩阵⽐例为 3*LEN : √3*LEN ,那么屏幕上的任意点(x, y)所对应的矩阵坐标为(int(x/(3*LEN)), int(y/(√3*LEN)))

第一步:设置矩形的长宽比例值TR、TB(TB:TR 符合比例 3:√3)其中长宽比为3:√3
第二步:获取纹理坐标的x,y;
第三步:根据纹理坐标计算对应的矩形坐标wx、wy假设矩阵的比例为3*len:√3*len,那么纹理坐标(x,y)对应的矩阵坐标为

图片来自网络

第四步:根据行列的奇偶情况,求对应的中心点纹理坐标v1、v2;
第五步:根据距离公式求像素点距离两个中心点的距离s1、s2;
第六步:根据求出的距离,判断离哪个中心点近,就取哪个六边形的中心点颜色值为六边形的颜色值;

  • 效果图


    六边形马赛克效果图
  • 六边形马赛克片元着色器代码

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const float MosaicSize = 0.03;
void main (void) {
    
    float length = MosaicSize;
    
    float TR = 0.866026;
    
    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
五、三角形马赛克的实现原理
  • 原理:三角形马赛克原理和六边形马赛克的类似,我们只需要将六边形平分六等分,六边形分为了6个等边三角形,然后按照角度值来判断坐标位于哪一个三角形内,就采用哪个哪个三角形的中心点颜色。


    三角形马赛克原理图解
  • 效果图
    三角形马赛克效果图
  • 三角形马赛克片元着色器代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const float MosaicSize = 0.03;
void main (void) {
    
    float length = MosaicSize;
    
    float TR = 0.866026;
    
    float TB = 1.5;
    
    float PI6 = 0.523599;
    
    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= 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;
}

源码地址MosaicDemo

你可能感兴趣的:(OpenGL ES --颠倒滤镜、灰度滤镜以及三种马赛克效果的实现原理探究)