OpenGL ES 案例:灰度+馬賽克濾鏡

本案例目的是理解如何用GLSL實(shí)現(xiàn)灰度+馬賽克濾鏡,最終實(shí)現(xiàn)效果

準(zhǔn)備工作的代碼與分屏濾鏡一樣, 頂點(diǎn)著色器也沒(méi)有任何變化,主要是片元著色器中的實(shí)現(xiàn)濾鏡算法

灰度濾鏡

實(shí)現(xiàn)原理是讓 RGB 值保持一個(gè)平衡并填充,或者只保留一個(gè)亮度值

  • 浮點(diǎn)算法:Gray = R0.3 + G0.59 + B*0.11(RGB的權(quán)重之和為1)
  • 整數(shù)算法:Gray = (R30 + G59 + B*11)/100(RGB的權(quán)重之和為100)
  • 移位算法:Gray = (R76 + G151 + B*28)>>8
  • 平均值法:Gray = (R+G+B)/3
  • 僅取綠色:Gray = G

浮點(diǎn)算法代碼案例

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

//RGB權(quán)重值
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);

void main(){
    //獲取對(duì)應(yīng)紋理坐標(biāo)系下色顏色值
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    
    //將顏色 mask 與 變換因子相乘得到灰度值
    float luminance = dot(mask.rgb, W);
    
    //將灰度值填充到像素中
    gl_FragColor = vec4(vec3(luminance), 1.0);
}

實(shí)現(xiàn)效果如下

正方形馬賽克

馬賽克效果就是將圖片中的一個(gè)相當(dāng)大小的區(qū)域用同一個(gè)點(diǎn)的顏色來(lái)填充,可以認(rèn)為是?規(guī)模的降低圖像的分辨率,?讓圖像的?些細(xì)節(jié)隱藏起來(lái)。

  • 根據(jù)紋理坐標(biāo)計(jì)算實(shí)際圖像中的位置
  • 計(jì)算一個(gè)小馬賽克的坐標(biāo)
  • 將馬賽克坐標(biāo)換算回紋理坐標(biāo)

代碼案例

precision highp float;
//紋理坐標(biāo)
uniform sampler2D Texture;
//紋理采樣器
varying vec2 TextureCoordsVarying;
//紋理圖片size
const vec2 TexSize = vec2(400.0, 400.0);
//馬賽克size
const vec2 MosaicSize = vec2(16.0, 16.0);

void main(){
    //計(jì)算實(shí)際圖像位置
    vec2 intXY = vec2(TextureCoordsVarying.x * TexSize.x, TextureCoordsVarying.y * TexSize.y);
    
    //floor(x) 內(nèi)建函數(shù),返回小于/等于x最大的整數(shù),即向下取整
    //floor(intXY.x/mosaicSize.x)*mosaicSize.x 計(jì)算出一個(gè)小馬賽克的坐標(biāo)
    vec2 XYMosaic = vec2(floor(intXY.x/MosaicSize.x)*MosaicSize.x, floor(intXY.y/MosaicSize.y)*MosaicSize.y);

    //換算回紋理坐標(biāo),此時(shí)的紋理坐標(biāo)是小馬賽克的部分的紋理坐標(biāo),即某一個(gè)色塊
    vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize.y);
    //獲取到馬賽克后的紋理坐標(biāo)的顏色值
    vec4 color = texture2D(Texture, UVMosaic);
    //將馬賽克顏色值賦值給gl_FragColor
    gl_FragColor = color;
}

六邊形馬賽克

六邊形馬賽克效果就是讓?張圖?,分割成由六邊形組成,讓每個(gè)六邊形中的顏?相同(直接取六邊形中?點(diǎn)像素RGB較?便,我們這?采?的就是這種?法),將它進(jìn)?分割,取每個(gè)六邊形的中?點(diǎn)畫出?個(gè)矩陣,如下

上圖中,畫出很多長(zhǎng)和寬比例為 3:√3的矩形陣。然后我們可以對(duì)每個(gè)點(diǎn)進(jìn)行編號(hào),如上圖中,采用坐標(biāo)系標(biāo)記。假如我們的屏幕的左上點(diǎn)為 (0, 0) 點(diǎn),則屏幕上的任意點(diǎn) (x, y) 就可以找到對(duì)應(yīng)的那個(gè)矩陣了。假定我們?cè)O(shè)定的矩陣比例為 3LEN : (√3LEN)(LEN為 六邊形的邊長(zhǎng)),那么屏幕上的任意點(diǎn) (x, y) 所對(duì)應(yīng)的矩陣坐標(biāo)為 (int(x/(3 * LEN)), int(y/(√3*LEN)))。

在上面的矩形圖中可以看到,一個(gè)矩形中只有兩個(gè)六邊形的中心點(diǎn),而且只有兩種情況,即矩形中的樣式 ”\“ 和 “/”,在 “/” 矩形中,六邊形的中心點(diǎn) (0, 0) & (1, 1);在 “\” 矩形中,六邊形的中心點(diǎn) (1, 0) & (0, 1)。如下所示

矩形中的四個(gè)點(diǎn)的坐標(biāo)計(jì)算如下

根據(jù)距離公式求像素點(diǎn)距離兩個(gè)中心點(diǎn)的距離s1, s2

s1 = √((v1.x-x)2 + (v1.y-y)2)
s2 = √((v2.x-x)2 + (v2.y-y)2)

根據(jù)距離,判斷離哪個(gè)中心點(diǎn)近,就取哪個(gè)六邊形的中心點(diǎn)顏色值為六邊形的顏色值。

代碼案例

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//六邊形的邊長(zhǎng)
const float mosaicSize = 0.03;

void main(){
    
    float length = mosaicSize;
    //矩形的高的比例為√3,取值 √3/2 ,也可以直接取√3
    float TR = 0.866025;
    //矩形的長(zhǎng)的比例為3,取值 3/2 = 1.5,也可以直接取3
    float TB = 1.5;
    
    //取出紋理坐標(biāo)
    float x = TextureCoordsVarying.x;
    float y = TextureCoordsVarying.y;
    
    //根據(jù)紋理坐標(biāo)計(jì)算出對(duì)應(yīng)的矩陣坐標(biāo) 
    //即 矩陣坐標(biāo)wx = int(紋理坐標(biāo)x/ 矩陣長(zhǎng)),矩陣長(zhǎng) = TB*len
    //即 矩陣坐標(biāo)wy = int(紋理坐標(biāo)y/ 矩陣寬),矩陣寬 = TR*len
    int wx = int(x / TB / length);
    int wy = int(y / TR / length);
    vec2 v1, v2, vn;
    
    //判斷wx是否為偶數(shù),等價(jià)于 wx % 2 == 0
    if (wx/2 * 2 == wx) {
        if (wy/2 * 2 == wy) {//偶行偶列
            //(0,0),(1,1)
            v1 = vec2(length * TB * float(wx), length * TR * float(wy));
            v2 = vec2(length * TB * float(wx+1), length * TR * float(wy+1));
        }else{//偶行奇列
            //(0,1),(1,0)
            v1 = vec2(length * TB * float(wx), length * TR * float(wy+1));
            v2 = vec2(length * TB * float(wx+1), length * TR * float(wy));
        }
    }else{
        if (wy/2 * 2 == wy) {//奇行偶列
            //(0,1),(1,0)
            v1 = vec2(length * TB * float(wx), length * TR * float(wy+1));
            v2 = vec2(length * TB * float(wx+1), length * TR * float(wy));
        }else{//奇行奇列
            //(0,0),(1,1)
            v1 = vec2(length * TB * float(wx), length * TR * float(wy));
            v2 = vec2(length * TB * float(wx+1), length * TR * float(wy+1));
        }
    }
    //利用距離公式,計(jì)算中心點(diǎn)與當(dāng)前像素點(diǎn)的距離
    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));
    
    //選擇距離小的則為六邊形的中心點(diǎn),且獲取它的顏色
    vn = (s1 < s2) ? v1 : v2;
    //獲取六邊形中心點(diǎn)的顏色值
    vec4 color = texture2D(Texture, vn);
    gl_FragColor = color;
}

三角形馬賽克

三角形馬賽克是由六邊形馬賽克演變而來(lái),即要先有六邊形才有三角形。根據(jù)當(dāng)前六邊形的中心點(diǎn)與六個(gè)頂點(diǎn)分為六個(gè)三角形,根據(jù)夾角判斷屬于哪個(gè)三角形,就將該三角形的中心點(diǎn)顏色作為整個(gè)三角形的文素。

相對(duì)六邊型增加的代碼案例

    //獲取像素點(diǎn)與中心點(diǎn)的角度
   float a = atan((x-vn.x)/(y-vn.y));
    
    //判斷夾角,屬于哪個(gè)三角形,則獲取哪個(gè)三角形的中心點(diǎn)坐標(biāo)
    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;
    }
    //獲取對(duì)應(yīng)三角形重心的顏色值
    vec4 color = texture2D(Texture, vn);
    // 將顏色值填充到片元著色器內(nèi)置變量gl_FragColor
    gl_FragColor = color;

完整代碼見(jiàn) GitHub 灰度+馬賽克濾鏡

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。