本案例目的是理解如何用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 灰度+馬賽克濾鏡