天天看點

OpenGLSL初探(六)使用GLSL實作濾鏡之灰階濾鏡、正方形馬賽克濾鏡、六邊形馬賽克濾鏡和三角形馬賽克濾鏡

此部落格隻為記錄濾鏡的算法,是以修改的隻是片元着色器代碼

注:具體代碼請檢視上一篇部落格:https://blog.csdn.net/weixin_40918107/article/details/107748865

1. 準備工作

  • 建立頂點着色器和片元着色器
  • 加載着色器,并連結
  • 建立圖層和上下文
  • 清空緩沖區,并設定渲染緩沖區與幀緩沖區
  • 開始繪制

2.濾鏡的實作

圖檔無濾鏡正常顯示的頂點着色器和片元着色器代碼。

注:以下代碼中的中文注釋在複制代碼時,請删除,否則會有未知的錯誤

頂點着色器:

//頂點坐标
attribute vec4 Position;
//紋理坐标
attribute vec2 TextureCoords;
//需要傳入片元着色器的紋理坐标
varying vec2 TextureCoordsVarying;

void main (void) {
    gl_Position = Position;
    TextureCoordsVarying = TextureCoords;
}
           

片元着色器:

//聲明高精度float
precision highp float;
//紋理
uniform sampler2D Texture;
//紋理坐标
varying vec2 TextureCoordsVarying;

void main (void) {
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    gl_FragColor = vec4(mask.rgb, 1.0);
}
           

2.1灰階濾鏡

灰階濾鏡實作的方案:

  • 浮點算法:Gray=R0.3+G0.59+B*0.11
  • 整數⽅法:Gray=(R30+G59+B*11)/100
  • 移位⽅法:Gray =(R76+G151+B*28)>>8;
  • 平均值法:Gray=(R+G+B)/3;
  • 隻取綠色:Gray=G;

一般使用第一個方案:浮點算法。

實作灰階濾鏡隻需要修改片元着色器即可,頂點着色器不變。

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//變換因子,即上方的浮點算法,此值取自GPUImage源碼
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);

void main (void) {
    //擷取對于紋理坐标下的顔⾊值
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    //将顔⾊mask 與 變換因⼦點乘得到灰階值.由向量轉變成标量
    float luminance = dot(mask.rgb, W);
    //vec4、vec3是内建函數,vec3是将顔色值RGB都設定成luminance
    gl_FragColor = vec4(vec3(luminance), 1.0);
}
           

2.2 正方形馬賽克濾鏡

⻢賽克效果就是把圖⽚的⼀個相當⼤⼩的區域⽤同⼀個點的顔⾊來表示.可以認為是⼤規模的降低圖像的分辨率,⽽讓圖像的⼀些細節隐藏起來。其實與臨近過濾非常的相似。

馬賽克效果是不可逆的,因為這是把原圖給修改了。

片元着色器代碼:

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

2.3六邊形馬賽克濾鏡

六邊形馬賽克效果就是讓⼀張圖⽚,分割成由六邊形組成,讓每

個六邊形中的顔⾊相同(直接取六邊形中⼼點像素RGB較⽅便,我們

這⾥采⽤的就是這種⽅法),将它進⾏分割,取每個六邊形的中⼼點畫出⼀個矩陣,如下

OpenGLSL初探(六)使用GLSL實作濾鏡之灰階濾鏡、正方形馬賽克濾鏡、六邊形馬賽克濾鏡和三角形馬賽克濾鏡

圖檔中的所有的黃點就是六邊形的中心點,每個六邊形的顔色就取其中心點的顔色。

我們把上圖中四個矩形單獨拿出進行下一步分析,如下圖:

OpenGLSL初探(六)使用GLSL實作濾鏡之灰階濾鏡、正方形馬賽克濾鏡、六邊形馬賽克濾鏡和三角形馬賽克濾鏡

思考:在左上角的長方形中,那個褐色的小點顯示的顔色應該顯示什麼顔色呢?

答案顯而易見應該與d2點的顔色一緻,這個點距離d2這個中心點比較近,是以是d2六邊形上點。

那麼在片元着色器上點的顔色的值就應該與該點距離最近的六邊形中心點的顔色一緻。

在上圖中我們還可以分析得到左上角的長方形隻有(0,0)和(1,1)這兩個點是六邊形的中心點,左下角的長方形隻有(0,1)和(1,0)這兩個點是六邊形的中心點。

并且在長方形線的的樣式隻有“\”和 “/”這兩種。

并且由六邊形的中心點組成的長方形有一個特點就是寬高比是3:√3,那麼假定我們設定的矩陣⽐例為 3LEN : √3LEN ,那麼螢幕上的任意點(x, y)所對應的矩陣坐标為(int(x/(3LEN)), int(y/

(√3LEN)))。那麼這四個點的坐标為:

OpenGLSL初探(六)使用GLSL實作濾鏡之灰階濾鏡、正方形馬賽克濾鏡、六邊形馬賽克濾鏡和三角形馬賽克濾鏡

片元着色器代碼:

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//六邊形的邊⻓
const float mosaicSize = 0.03;

void main (void)
{
    float length = mosaicSize;
    //sqrt(3)/2;
    float TR = 0.866025;
    float TB = 1.5;
    //紋理坐标:(0,0)(0,1)(1,0)(1,1)
    float x = TextureCoordsVarying.x;
    float y = TextureCoordsVarying.y;
    //wx,wy -> 表示紋理坐标在所對應的矩陣坐标為
    int wx = int(x / TB / length);
    int wy = int(y / TR / length);
    vec2 v1, v2, vn;
    
    //判斷wx,wy在矩形中的上半部還是下半部
    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 中
    gl_FragColor = color;
    
}
           

3.4 三角形馬賽克濾鏡

三角形馬賽克濾鏡是在六邊形濾鏡的基礎上,将六邊形分成6個相同的三角形。

這樣我們隻需要再進一步的分析,我們需要根據目前點與六邊形的中心點之間的角度來确定屬于哪個三角,然後再擷取三角形的中心點的顔色進行指派。

OpenGLSL初探(六)使用GLSL實作濾鏡之灰階濾鏡、正方形馬賽克濾鏡、六邊形馬賽克濾鏡和三角形馬賽克濾鏡

片元着色的代碼:

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//六邊形的邊⻓
const float mosaicSize = 0.03;

void main (void)
{
    float length = mosaicSize;
    //sqrt(3)/2;
    float TR = 0.866025;
    float TB = 1.5;
    //紋理坐标:(0,0)(0,1)(1,0)(1,1)
    float x = TextureCoordsVarying.x;
    float y = TextureCoordsVarying.y;
    //wx,wy -> 表示紋理坐标在所對應的矩陣坐标為
    int wx = int(x / TB / length);
    int wy = int(y / TR / length);
    vec2 v1, v2, vn;
    
    //判斷wx,wy在矩形中的上半部還是下半部
    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);
    //擷取a與紋理中⼼的⻆度.
    //atan算出的範圍是-180⾄180度,對應的數值是-PI⾄PI
    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);
  
  //判斷夾⻆a 屬于哪個三⻆形.則擷取哪個三⻆形的中⼼點坐标
    if (a >= PI6 && a < PI6 * 3.0) {
    //[30,90]
        vn = area1;
    } else if (a >= PI6 * 3.0 && a < PI6 * 5.0) {//[90,150]
        vn = area2;
    } else if ((a >= PI6 * 5.0 && a <= PI6 * 6.0)|| (a<-PI6 * 5.0 && a>-PI6*6.0)) {//[150,180],[-150,-180]
        vn = area3;
    } else if (a < -PI6 * 3.0 && a >= -PI6 * 5.0) {//[-90,-150]
        vn = area4;
    } else if(a <= -PI6 && a> -PI6 * 3.0) {//[-30,-90]
        vn = area5;
    } else if (a > -PI6 && a < PI6)
    {//[-30,30]
        vn = area6;
    }
    //擷取對應三⻆形中⼼的顔⾊值
    vec4 color = texture2D(Texture, vn);
    //将顔⾊值填充到⽚元着⾊器内置變量gl_FragColor
    gl_FragColor = color;
    
}
           

繼續閱讀