「⻢賽克效果」就是把圖⽚的⼀個至關⼤⼩的區域⽤同⼀個點的顏⾊來表示,能夠認爲是⼤規模的下降圖像的分辨率,⽽讓圖像的⼀些細節隱藏起來。web
無馬賽克濾鏡
「無濾鏡」效果的實現準備工做的代碼與「無分屏濾鏡」中的實現邏輯和流程一致,只須要修改相應的底部item數組及對應的着色器名稱等,這裏再也不說明這部份內容,頂點着色器也沒有任何變化,主要是針對片元着色器中GLSL代碼的實現濾鏡算法作具體的說明和實現;算法
具體流程請參考: OpenGL ES 之GLSL 實現「分屏濾鏡」效果數組
方形馬賽克濾鏡
1、效果展現
2、實現原理
濾鏡算法
根據紋理座標計算實際圖像中的位置,至關於將紋理顏色區放大;微信
計算出一個小馬賽克的座標,即找到馬賽克提取顏色值的像素點;app
將馬賽克座標換算回紋理座標,即將紋理顏色區縮小;編輯器
計算過程
計算圖像的實際位置
假設紋理座標爲(0,0)、(1,0)、(0,1)、(1,1), 紋理圖片的大小爲(400,400), 那麼實際圖片的位置爲(0,0)、(0,400)、(400,0)、(400,400);函數計算馬賽克實際位置
假設當前圖片的位置爲(400,400), 單個馬賽克的大小爲(100,100), 那麼當前馬賽克的座標爲:(floor(400/100100), floor(400/100100)) = (400, 400);學習計算馬賽克紋理座標
將馬賽座標換算成紋理座標,即(400/400, 400/400) = (1, 1), 根據texture2D函數獲得紋理座標的顏色值, 即當前馬賽克的顏色值;flex一個正方形馬賽克是由多個小的正方形圖像色塊組成, 馬賽克的顏色值是取其中一個小正方形的顏色值, 在floor(x)函數向下取整時, 就獲取了提取顏色的小方塊的像素點;google
3、GLSL實現
precision highp float;
// 紋理座標
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(){
// 計算實際圖像位置
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;
}
六邊形馬賽克濾鏡
1、效果展現
2、實現原理
六邊形馬賽克原理:將一張圖片,分割成由六邊形組成,再取每一個六邊形的重點畫出一個個的矩形,根據矩形的奇偶排列狀況求出對應的2箇中心點,並計算紋理座標與兩個中心點的距離,根據距離判斷,採起就近原則,當前的六邊形就採用近的中心點的顏色值。
將圖片分割成六邊形,六邊形中心點畫出矩形後的呈現以下所示:
濾鏡算法
設置矩形的長寬比例值TR、TB(TB:TR 符合比例 3:3*sqrt(3),其中長寬比爲3:3*sqrt(3),計算過程以下:
獲取紋理座標的x,y;
根據紋理座標計算對應的矩形座標wx、wy。假設矩陣的比例爲3*len:sqrt(3),那麼紋理座標(x,y)對應的矩陣座標爲
根據行列的奇偶狀況,求對應的中心點紋理座標v一、v2
偶行偶列:(0,0)(1,1),即左上、右下
偶行奇列:(0,1)(1,0),即左下、右上
奇行偶列:(0,1)(1,0),即左下、右上
奇行奇列:(0,0)(1,1),即左上、右下
最終只有兩種狀況,(0,0)(1,1) 和 (0,1)(1,0),以下圖所示
其中單個矩陣中,4個點的座標計算公式以下:
對於計算中的wx+1,拿(1,0)點來講,wx+1等同於(1,0)與(0,0)之間相差一個矩形的長,這個長度爲1,爲了獲得(1,0)點的座標,要在(0,0)點座標的基礎上,將wx增長一個長;
對於計算中的wy+1,拿(0,1)點來講,wy+1等同於(0,0)與(0,1)之間相差一個矩形的高,這個長度爲1,爲了獲得(0,1)點的座標,要在(0,0)點座標的基礎上,將wy增長一個高;
根據距離公式求像素點距離兩個中心點的距離s一、s2
根據求出的距離,判斷離哪一個中心點近,就取哪一個六邊形的中心點顏色值爲六邊形的顏色值;
3、GLSL實現
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
// 六邊形的邊長
const float mosaicSize = 0.03;
void main() {
float length = mosaicSize;
// 矩形的高的比例爲√3,取值 √3/2 ,也能夠直接取√3
float TR = 0.866025;
// 矩形的長的比例爲3,取值 3/2 = 1.5,也能夠直接取3
float TB = 1.5;
// 取出紋理座標
float x = TextureCoordsVarying.x;
float y = TextureCoordsVarying.y;
// 根據紋理座標計算出對應的矩陣座標
// 即矩陣座標 wx = int(紋理座標x/ 矩陣長),矩陣長 = TB*len
// 即矩陣座標 wy = int(紋理座標y/ 矩陣寬),矩陣寬 = TR*len
int wx = int(x / TB / length);
int wy = int(y / TR / length);
vec2 v1, v2, vn;
// 判斷wx是否爲偶數,等價於 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));
}
}
// 利用距離公式,計算中心點與當前像素點的距離
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));
// 選擇距離小的則爲六邊形的中心點,且獲取它的顏色
vn = (s1 < s2) ? v1 : v2;
// 獲取六邊形中心點的顏色值
vec4 color = texture2D(Texture, vn);
gl_FragColor = color;
}
三角形馬賽克濾鏡
1、效果展現
2、實現原理
原理:三角形馬賽克是由六邊形馬賽克演變而來,獲得三角形的前提,就是先有六邊形,而後將正六邊形6等分,每一個三角形都是正三角形,而後求出紋理座標與中心點的夾角,同時求出三角形的中心點,根據夾角判斷,夾角屬於哪一個三角形,就將該三角形的中心點顏色做爲整個三角形的紋素;
三角形濾鏡算法是在六邊形濾鏡算法的步驟上再增長如下步驟:
求出當前像素點與紋理中心點的夾角:以下圖所示,紋理座標爲(x,y),中心點爲vn,求夾角
計算6個三角形的中心點
判斷夾角屬於哪一個三角形,則獲取哪一個三角形的中心點座標,其中不一樣三角形的夾角範圍如圖所示
3、GLSL實現
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
gl_FragColor = color;
}
注意: atan 是 GLSL 中的內建函數,有兩種計算方式
atan(y,x) 值域是[0,π],
atan(y/x),值域是[-π/2, π/2] 。
原文連接:https://blog.csdn.net/forever_wj/article/details/107923371
-- END --
進技術交流羣,掃碼添加個人微信:Byte-Flow
獲取視頻教程和源碼
推薦:
FFmpeg + OpenGL ES 實現 3D 全景播放器
以爲不錯,點個在看唄~
本文分享自微信公衆號 - 字節流動(google_developer)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。