OpenGL ES 馬賽克濾鏡

灰度濾鏡效果就是讓一張彩色的圖片變成灰白的。

灰度濾鏡公式

  • 浮點(diǎn)算法:Gray=R*0.3+G*0.59+B*0.11
  • 整數(shù)?法:Gray=(R*30+G*59+B*11)/100
  • 移位?法:Gray =(R*76+G*151+B*28)>>8;
  • 平均值法:Gray=(R+G+B)/3;
  • 僅取綠?:Gray=G凄硼;

代碼實(shí)現(xiàn)

  • 我們首先設(shè)定一個(gè)權(quán)重懦傍,借鑒了GPUImage浪感,綠色值最高是因?yàn)槿藗儗G色敏感度最高
const highp vec3 W = vec3(0.2125,0.7154,0.0721);

  • 然后計(jì)算出紋素中的灰度值,dot指的是點(diǎn)乘
//計(jì)算灰度值
float luminance = dot(mask.rgb,W);

  • 最后將灰度值轉(zhuǎn)化為紋素返回給gl_FragColor
gl_FragColor = vec4(vec3(luminance),0.1);

  • 整體片元著色器代碼如下:
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const highp vec3 W = vec3(0.2125,0.7154,0.0721);

void main (void) {
    //獲取對應(yīng)紋理坐標(biāo)系下色顏色值
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    //獲取灰度值
    float luminance = dot(mask.rgb,W);
    //將灰度值轉(zhuǎn)化成顏色值
    gl_FragColor = vec4(vec3(luminance),0.1);
}

正方形馬賽克

圖片選自楓紫

原理

馬賽克效果就是把圖?的?個(gè)相當(dāng)??的區(qū)域?同?個(gè)點(diǎn)的顏?來表示,從而隱藏關(guān)鍵信息。

我們可以把圖片切割成無數(shù)的小的框框,然后再取小框框中某個(gè)點(diǎn)的顏色然后再把取出去的顏色整體覆蓋到框框中麻顶,就會出現(xiàn)一個(gè)色塊一個(gè)色塊的效果赦抖,就呈現(xiàn)出了馬賽克的效果

代碼實(shí)現(xiàn)

  • 首先,假定紋理的大小和馬賽克的大小馬賽克設(shè)置的越小辅肾,馬賽克越密集
//假設(shè)紋理大小為400.0 * 400.0
const vec2 TextSize = vec2(400.0,400.0);
//設(shè)置馬賽克大小為10.0 * 10.0
const vec2 MosaicSize = vec2(10.0,10.0);

  • 獲取馬賽克在假設(shè)紋理中的實(shí)際坐標(biāo)
//獲取紋理中的實(shí)際坐標(biāo)
vec2 intXY = vec2(TextureCoordsVarying.x * TextSize.x , TextureCoordsVarying.y * TextSize.y);

  • 計(jì)算一個(gè)小馬賽克的坐標(biāo)
//計(jì)算小馬賽克的坐標(biāo)
vec2 XYMosaic = vec2(floor(intXY.x/MosaicSize.x) * MosaicSize.x , floor(intXY.y/MosaicSize.y) * MosaicSize.y);

floor()是glsl的一個(gè)內(nèi)建函數(shù),返回?于/等于X的最?整數(shù)值队萤,即向下取整。

  • 最后換算出在紋理坐標(biāo)中的位置
//獲取紋理坐標(biāo)中的位置
vec2 UVMosaic = vec2(XYMosaic.x / TextSize.x , XYMosaic.y / TextSize.y);

  • 賦值給gl_FragColor
    vec4 mask = texture2D(Texture, UVMosaic);
    gl_FragColor = mask;

  • 整體代碼如下:
precision highp float;
uniform sampler2D Texture;
const vec2 TextSize = vec2(400.0,400.0);
const vec2 MosaicSize = vec2(10.0,10.0);
varying vec2 TextureCoordsVarying;

void main (void) {
    vec2 intXY = vec2(TextureCoordsVarying.x * TextSize.x , TextureCoordsVarying.y * TextSize.y);
    vec2 XYMosaic = vec2(floor(intXY.x/MosaicSize.x) * MosaicSize.x , floor(intXY.y/MosaicSize.y) * MosaicSize.y);
    vec2 UVMosaic = vec2(XYMosaic.x / TextSize.x , XYMosaic.y / TextSize.y);
    vec4 mask = texture2D(Texture, UVMosaic);
    gl_FragColor = mask;
}

正六邊形馬賽克

原理

image
首先我們設(shè)置舉行的長寬:畫出很多?和寬?例為 3:√3 的的矩形陣矫钓。然后我們可以對每個(gè)點(diǎn)進(jìn)?編號,假如我們的屏幕的左上點(diǎn)為上圖的(0,0)點(diǎn)要尔,則屏幕上的任?點(diǎn)我們找到它所對應(yīng)的那個(gè)矩形了,假定我們設(shè)定的矩陣?例為 3LEN : √3LEN ,那么屏幕上的任意點(diǎn)(x, y)所對應(yīng)的矩陣坐標(biāo)為(int(x/(3LEN)), int(y/(√3LEN)))新娜。wx,wy -> 表?紋理坐標(biāo)在所對應(yīng)的矩陣坐標(biāo)為int wx = int(x /( 1.5 * length)); int wy = int(y /(TR * length))赵辕。
3:√3的來源:設(shè)置矩形的長寬比例值TR、TB(TB:TR 符合比例 3:√3)計(jì)算過程如下:

image

獲取紋理坐標(biāo)的x杯活,y匆帚,根據(jù)紋理坐標(biāo)計(jì)算對應(yīng)的矩形坐標(biāo)wx、wy旁钧,假設(shè)矩陣的比例為3len:√3len,那么紋理坐標(biāo)(x互拾,y)對應(yīng)的矩陣坐標(biāo)為:

image

根據(jù)行列的奇偶情況歪今,求對應(yīng)的中心點(diǎn)紋理坐標(biāo)v1、v2

偶行偶列:(0颜矿,0)(1寄猩,1)/,即左上骑疆、右下

    偶行奇列:(0田篇,1)(1,0)\箍铭,即左下泊柬、右上

    奇行偶列:(0,1)(1诈火,0)\兽赁,即左下、右上

    奇行奇列:(0冷守,0)(1刀崖,1)/,即左上拍摇、右下

    最終就兩種情況:
image
image
    最終匯總起來也只有2種情況亮钦,(0,0)(1充活,1) 和 (0蜂莉,1)(1蜡娶,0),如下圖所示
image

其中單個(gè)矩陣中巡语,4個(gè)點(diǎn)的坐標(biāo)計(jì)算公式如下:

  • 對于計(jì)算中的wx+1翎蹈,拿(1,0)點(diǎn)來說男公,wx+1等同于(1荤堪,0)與(0,0)之間相差一個(gè)矩形的長枢赔,這個(gè)長度為1澄阳,為了得到(1,0)點(diǎn)的坐標(biāo)踏拜,要在(0碎赢,0)點(diǎn)坐標(biāo)的基礎(chǔ)上,將wx增加一個(gè)長

  • 對于計(jì)算中的wy+1速梗,拿(0肮塞,1)點(diǎn)來說,wy+1等同于(0姻锁,0)與(0枕赵,1)之間相差一個(gè)矩形的高,這個(gè)長度為1位隶,為了得到(0拷窜,1)點(diǎn)的坐標(biāo),要在(0涧黄,0)點(diǎn)坐標(biāo)的基礎(chǔ)上篮昧,將wy增加一個(gè)高

image

最后根據(jù)勾股定理,計(jì)像素點(diǎn)距離兩個(gè)中心點(diǎn)的距離s1笋妥、s2根據(jù)距離公式求像素點(diǎn)距離兩個(gè)中心點(diǎn)的距離s1懊昨、s2

image

作者:楓紫_6174
鏈接:http://www.reibang.com/p/e390baf1089b
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)挽鞠,非商業(yè)轉(zhuǎn)載請注明出處疚颊。

 //TR = √3/2
 int wx = int(x /( 1.5 * length)); 
 int wy = int(y /(TR * length));

所以換算出坐標(biāo)

image
  • 左上角,vec2(length * 1.5 * float(wx), length * TR * float(wy))
  • 左下角信认,vec2(length * 1.5 * float(wx), length * TR * float(wy + 1))
  • 右上角材义,vec2(length * 1.5 * float(wx + 1), length * TR * float(wy))
  • 右下角,vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1))

所以我們只需要判斷紋理對應(yīng)的點(diǎn)離哪個(gè)中心點(diǎn)近即可

image

判斷C離A點(diǎn)近還是B點(diǎn)近嫁赏,我們只需要求出C到A和C到B的距離即可其掂,

//pow是(v1.x - x)的平方
//sqrt是開根號
float s = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));

現(xiàn)在我們要判斷4種情況,如圖

image

分別為:

  • 偶行偶列潦蝇,A
//中心點(diǎn)的坐標(biāo)分別為
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));

  • 偶行奇列款熬,B
//中心點(diǎn)的坐標(biāo)分別為
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));

  • 奇行偶列深寥,C
//中心點(diǎn)的坐標(biāo)分別為
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));

  • 奇行奇列,D
//中心點(diǎn)的坐標(biāo)分別為
v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));

最后進(jìn)行比較贤牛,將小的點(diǎn)的坐標(biāo)當(dāng)成改區(qū)域坐標(biāo)即可
完整代碼如下:

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

三角形馬賽克

之前我們已經(jīng)計(jì)算出了正六邊形馬賽克惋鹅,根據(jù)我們紋理的坐標(biāo)可以知道究竟在哪個(gè)六邊形里面,那么三角形就是在六邊形的基礎(chǔ)上進(jìn)行細(xì)分殉簸,將正六邊形切割成六塊闰集,判斷點(diǎn)究竟在哪個(gè)三角形里面,如圖

image
  • 首先求出紋理坐標(biāo)點(diǎn)和中心點(diǎn)之間的夾角
float a = atan((x - vn.x)/(y - vn.y));

  • 分別求出六個(gè)區(qū)域中心點(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);

  • 判斷紋理坐標(biāo)在哪個(gè)區(qū)域
    const float PI6 = 0.523599;
    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;
    }

  • 完整代碼如下
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const float mosaicSize = 0.03;

void main (void) {
    float length = mosaicSize;
    const float PI6 = 0.523599;
    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) {
            //(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);
    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;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末般卑,一起剝皮案震驚了整個(gè)濱河市武鲁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝠检,老刑警劉巖沐鼠,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叹谁,居然都是意外死亡饲梭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門焰檩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來排拷,“玉大人,你說我怎么就攤上這事锅尘。” “怎么了布蔗?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵藤违,是天一觀的道長。 經(jīng)常有香客問我纵揍,道長顿乒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任泽谨,我火速辦了婚禮璧榄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吧雹。我一直安慰自己骨杂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布雄卷。 她就那樣靜靜地躺著搓蚪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丁鹉。 梳的紋絲不亂的頭發(fā)上妒潭,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天悴能,我揣著相機(jī)與錄音,去河邊找鬼雳灾。 笑死漠酿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谎亩。 我是一名探鬼主播炒嘲,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼团驱!你這毒婦竟也來了摸吠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤嚎花,失蹤者是張志新(化名)和其女友劉穎寸痢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體紊选,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡啼止,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兵罢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片献烦。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卖词,靈堂內(nèi)的尸體忽然破棺而出巩那,到底是詐尸還是另有隱情,我是刑警寧澤此蜈,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布即横,位于F島的核電站,受9級特大地震影響裆赵,放射性物質(zhì)發(fā)生泄漏东囚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一战授、第九天 我趴在偏房一處隱蔽的房頂上張望页藻。 院中可真熱鬧,春花似錦植兰、人聲如沸份帐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弥鹦。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間彬坏,已是汗流浹背朦促。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栓始,地道東北人务冕。 一個(gè)月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像幻赚,于是被迫代替她去往敵國和親禀忆。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354