Swift OpenGL ES 自定义常用滤镜(一)

前面几篇文章已经详细介绍了OpenGL以及OpenGL ES的基本使用、加载一张图片、加载三维立体图像等,学习使用OpenGL的最终主要目的就是处理图片滤镜,视频滤镜,

 常见的一些视频/图像的处理app基本上都是使用OpenGLES实现的,本篇介绍学习自定义一些常用滤镜以及实现原理,主要是顶点着色器程序和片元着色器程序,大部分色滤镜都是顶点着色器进行

1.分屏滤镜,二三四六九分屏,分屏滤镜主要是改变像素的纹素,通过坐标显示对应映射坐标的纹素

分屏滤镜就是处理像素点映射到想要显示的像素点的纹素值,可以从顶点着色器处理也可以在片元着色器中处理

//二分屏顶点着色器代码:
         attribute vec4 Position;
         attribute vec2 TextureCoords;
         varying vec2 TextureCoordsVarying;

         void main (void) {
             gl_Position = Position;
             TextureCoordsVarying = TextureCoords;
         }
         //二分屏片元着色器代码:
         precision highp float;
         uniform sampler2D Texture;
         varying highp vec2 TextureCoordsVarying;

         void main() {
             vec2 uv = TextureCoordsVarying.xy;
             float y;
             if (uv.y >= 0.0 && uv.y <= 0.5) {
                 y = uv.y + 0.25;
             } else {
                 y = uv.y - 0.25;
             }
             gl_FragColor = texture2D(Texture, vec2(uv.x, y));
         }
//三分屏顶点着色器代码
         attribute vec4 Position;
         attribute vec2 TextureCoords;
         varying vec2 TextureCoordsVarying;

         void main (void) {
             gl_Position = Position;
             TextureCoordsVarying = TextureCoords;
         }
         //三分屏片元着色器代码
         uniform sampler2D Texture;
         varying highp vec2 TextureCoordsVarying;

         void main() {
             vec2 uv = TextureCoordsVarying.xy;
             float y;
             if (uv.y >= 0.0 && uv.y <= 0.33) {
                 y = uv.y + 0.33;
             }else if (uv.y > 0.66 && uv.y <= 1.0){
                 y = uv.y - 0.33;
             }else{
                 y = uv.y;
             }
             gl_FragColor = texture2D(Texture, vec2(uv.x, y));
         }

//四分屏顶点着色器
        let verstr = """
             attribute vec4 Position;
             attribute vec2 TextureCoords;
             varying vec2 TextureCoordsVarying;
             void main (void) {
             gl_Position = Position;
             TextureCoordsVarying = TextureCoords;
             }
         """
        //四分屏片元着色器程序代码
        let fragStr = """
            precision highp float;
            uniform sampler2D Texture;
            varying highp vec2 TextureCoordsVarying;

            void main() {
            vec2 uv = TextureCoordsVarying.xy;
            float y;
            float x;
            if (uv.y >= 0.0 && uv.y <= 0.5) {
                y = uv.y * 2.0;
            } else {
                y = (uv.y - 0.5) * 2.0;
            }
            if (uv.x >= 0.0 && uv.x <= 0.5) {
                x = uv.x * 2.0;
            } else {
                x = (uv.x - 0.5) * 2.0;
            }
            
            gl_FragColor = texture2D(Texture, vec2(x, y));
            }
       """
//六分屏片元着色器代码,顶点着色器和前面的一样
        let fsh6 = """
          precision highp float;
          uniform sampler2D Texture;
          varying highp vec2 TextureCoordsVarying;

         void main(){
         vec2 uv = TextureCoordsVarying.xy;
          if (uv.x < 0.3333) {
          uv.x = uv.x * 3.0;
         }else if(uv.x > 0.6666){
          uv.x = (uv.x - 0.6666) * 3.0;
         }else{
          uv.x = (uv.x - 0.3333) * 3.0;
         }

         if (uv.y < 0.5){

            uv.y = uv.y * 2.0;
         }else{
            uv.y = (uv.y - 0.5) * 2.0;
         }
         gl_FragColor = texture2D(Texture, uv);

         }

//九分屏片元着色器程序代码
        let fsh9 = """
         precision highp float;
         uniform sampler2D Texture;
         varying highp vec2 TextureCoordsVarying;

        void main(){
        vec2 uv = TextureCoordsVarying.xy;
         if (uv.x < 0.3333) {
         uv.x = uv.x * 3.0;
        }else if(uv.x > 0.6666){
         uv.x = (uv.x - 0.6666) * 3.0;
        }else{
         uv.x = (uv.x - 0.3333) * 3.0;
        }

        if (uv.y < 0.3333){
           uv.y = uv.y * 3.0;
        }else if(uv.y < 0.6666){
           uv.y = (uv.y - 0.3333) * 3.0;
        }else {
           uv.y = (uv.y - 0.6666) * 3.0;
        }
        gl_FragColor = texture2D(Texture, uv);
        }
        """

2.灰度滤镜,就是使rgb三色的平衡,0.2125, 0.7154, 0.0721,人眼对绿色比较敏感,所以绿色值更大一些

         灰度滤镜有多种实现方法:

         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

let maskFsh = """
        precision highp float;
        uniform sampler2D Texture;
        varying highp vec2 TextureCoordsVarying;
        const highp vec3 w = vec3(0.2125, 0.7154, 0.0721);

        void main(){
        vec4 color = texture2D(Texture,TextureCoordsVarying);
        float color1 = dot(color.rgb,w);
        gl_FragColor = vec4(vec3(color1),1.0);
        }
        """

2.漩涡滤镜,给定中心点、半径,旋转角度,距离中心点约近旋转角度越大,坐标某点的颜色值等于旋转之后的纹素颜色值,图像漩涡主要是在某个半径范围里,把当前采样点旋转 ⼀定⻆角度,旋转以后当前点的颜色就被旋转后的点的颜色代替,因此整个半径范围里会有旋转的效果。如果旋 转的时候旋转⻆角度随着当前点离半径的距离递减,整个图像就会出现漩涡效果。这⾥使⽤了了抛物线递减因 子:(1.0-(r/Radius)*(r/Radius))。

//漩涡滤镜片元着色器代码:
         precision mediump float; //PI
         const float PI = 3.14159265; //纹理理采样器器
         uniform sampler2D Texture; //旋转⻆角度
         const float uD = 80.0; //旋涡半径
         const float uR = 0.5;
         //纹理理坐标
         varying vec2 TextureCoordsVarying;
         void main() {
         //旋转正⽅方形范围:[512,512]
         ivec2 ires = ivec2(512, 512); //获取旋转的直径
         float Res = float(ires.s); //纹理理坐标[0,0],[1,0],[0,1],[1,1]...
         vec2 st = TextureCoordsVarying; //半径 = 直径 * 0.5;
         float Radius = Res * uR;
         //准备旋转处理理的纹理理坐标 = 纹理理坐标 * 直径 vec2 xy = Res * st;
         //纹理理坐标减去中点
         vec2 dxy = xy - vec2(Res/2., Res/2.);
         //r 半径 即跟中点的距离
         float r = length(dxy);
         //抛物线递减因⼦子:(1.0-(r/Radius)*(r/Radius) )  衰减因子为二次函数 
         float beta = atan(dxy.y, dxy.x) + radians(uD) * (1.0-(r/Radius)*(r/Radius));
         if(r<=Radius) {
         //获取的纹理理坐标旋转beta度.
         xy = Res/2.0 + r*vec2(cos(beta), sin(beta));
         }
         //st = 旋转后的纹理理坐标/旋转范围 st = xy/Res;
         //将旋转的纹理理坐标替换原始纹理理坐标TextureCoordsVarying 获取对应像素点的颜⾊色. vec3 irgb = texture2D(Texture, st).rgb;
         //将计算后的颜⾊色填充到像素点中 gl_FragColor
         gl_FragColor = vec4( irgb, 1.0 ); }

3.马赛克滤镜,马赛克滤镜就是某一小半径大小的圆内的颜色值的相同,都取圆心的颜色值,

马赛克效果就是把图片的一个相当⼤⼩的区域⽤同一个 点的颜色来表示.可以认为是大规模的降低图像的分辨 率,⽽而让图像的⼀一些细节隐藏起来。

 //马赛克滤镜片元着色器代码:
         on 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的最⼤大整数值.
         // 0123456789  假如m大小为3 ,floor(x/3)* 3,结果,012取0 345取3 678 取6,就形成了某一片是一个文素的颜色,形成马赛克
         // 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;
         }

4.六边形马赛克

         滤镜实现思路: 我们要做的效果就是让一张图片,分割成由六边形组成,让每个六边形中的颜色相同(直接取六边形中⼼点像素RGB较⽅便,我们 这里采⽤的就是这种⽅方法)将它进⾏行行分割,取每个六边形的中⼼点画出⼀个六边形,如下图:

如上图,画出很多长和宽比例为 2:√3 的的矩形阵。然后我们可以对每个点进行编号,如上图中,采⽤用坐标系标记.

假如我们的屏幕的左上点为上图的(0,0)点,则屏幕上的任⼀点我们找到它所对应的那个矩形了了。 假定我们设定的矩阵⽐例例为 2*LEN : √3*LEN ,那么屏幕上的任意 点(x, y)所对应的矩阵坐标为(int(x/(2*LEN)), int(y/ (√3*LEN)))。

 //wx,wy -> 表示纹理坐标在所对应的矩阵坐标为 int wx = int(x /( 1.5 * length)); int wy = int(y /(TR * length));

//六边形马赛克片元着色器代码
         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;
             
         }

 5.三角形马赛克

         三角形马赛克和六边形马赛克原理类是,理解了六边形马赛克实现原理,三角形就是把六边形分成了六个三角形,每个三角形内的颜色值取同一个。

//三角形马赛克的片元着色器代码:
         precision highp float;
         uniform sampler2D Texture;
         varying vec2 TextureCoordsVarying;

         float mosaicSize = 0.03;

         void main (void){
             const float TR = 0.866025;
             const float PI6 = 0.523599;
             
             float x = TextureCoordsVarying.x;
             float y = TextureCoordsVarying.y;
             
          
             int wx = int(x/(1.5 * mosaicSize));
             int wy = int(y/(TR * mosaicSize));
             
             vec2 v1, v2, vn;
             
             if (wx / 2 * 2 == wx) {
                 if (wy/2 * 2 == wy) {
                     v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy));
                     v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * TR * float(wy + 1));
                 } else {
                     v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy + 1));
                     v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * TR * float(wy));
                 }
             } else {
                 if (wy/2 * 2 == wy) {
                     v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy + 1));
                     v2 = vec2(mosaicSize * 1.5 * float(wx+1), mosaicSize * TR * float(wy));
                 } else {
                     v1 = vec2(mosaicSize * 1.5 * float(wx), mosaicSize * TR * float(wy));
                     v2 = vec2(mosaicSize * 1.5 * float(wx + 1), mosaicSize * 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;
         }

由于篇幅过长还有一部分滤镜效果见下一篇,所有滤镜效果以及加载方法等详细代码github地址:https://github.com/duzhaoquan/ImagesVideoFilters.git

posted @ 2020-06-23 13:53  不停奔跑的蜗牛  阅读(624)  评论(0编辑  收藏  举报