OpenGL ES - 旋渦、馬賽克濾鏡

前言

上篇文章已經學習了分屏濾鏡的實現,本篇文章主要講的是旋渦和馬賽克濾鏡,固然也包括了一些常見的濾鏡-灰度濾鏡、翻轉濾鏡。仍是從簡單的提及吧。git

簡單濾鏡

灰度濾鏡

咱們使用蘋果手機拍照的時候有個特效是黑白的,在一些常見的App上都有灰度濾鏡效果。在咱們瞭解圖片的色彩後會知道灰度濾鏡會是很是的簡單。這裏有篇文章能夠看下顏色編碼:YUV的那些事。圖片灰度最簡單的辦法就是顏色中的RGB份量都是用G的值,該圖片就是灰度圖片。下面有五種方法進行灰度計算:github

浮點算法 Gray = Gray = R * 0.3 + G * 0.59 + B * 0.11算法

整數算法 Gray = (R * 30 + G * 59 + B * 11) / 100markdown

移位算法 Gray = (R * 76 + G * 151 + B * 28) >> 8函數

平均值法 Gray = (R + G + B) / 3;oop

取G值法 Gray = G;post

下面咱們使用平均值方法來作灰度濾鏡的處理學習

precision highp float;
varying vec2 textureCoord;
uniform sampler2D sampler;

void main(){
    vec4 mask = texture2D(sampler, textureCoord);
    float color = (mask.r + mask.g + mask.b) / 3.0;
    gl_FragColor = vec4(color, color, color, 1.0);
}
複製代碼

翻轉濾鏡

我最開始的念頭就是在矯正紋理的時候不矯正不就獲得了一張翻轉圖嘛😃,可是理性告訴我不能這樣,否則其餘濾鏡咋整。這個濾鏡和上面的那個濾鏡能夠說同樣的簡單。編碼

varying lowp vec2 textureCoord;
uniform sampler2D sampler;

void main(){
    gl_FragColor = texture2D(sampler, vec2(textureCoord.x, 1.0 - textureCoord.y));
}
複製代碼

旋渦濾鏡

旋渦濾鏡在咱們的抖音也有,旋渦這個濾鏡有點複雜,甚至比蜂型的馬賽克還難理解。咱們先看一張效果圖。看完了這張慘不忍睹的圖片後咱們在來看看旋渦濾鏡的原理。spa

利用了一個拋物線衰減公式:(1.0 - (r / Radius) * (r / Radius)) 。假設咱們在一塊區域內旋轉採樣點,這塊區域都會有旋轉的效果,若是咱們在旋轉的過程當中加入這個衰減的因子,那麼就會形成咱們看到的這張圖片的模樣。

precision highp float;
varying vec2 textureCoord;
uniform sampler2D sampler;

const float PI = 3.1415926;
const float angle = 80.0;
const float radius = 0.3;

void main(){
    
    // 旋轉正方形範圍
    vec2 rectSize = vec2(0.5, 0.5);
    // 旋轉區域的內切圓直徑
    float votexDiameter = rectSize.s;
    // 紋理座標
    vec2 st = textureCoord;
    // 旋渦的半徑
    float votexRadius = votexDiameter * radius;
    vec2 textureCoordinate = st * votexDiameter;
    vec2 textureCoordinateHalf = textureCoordinate - vec2(votexDiameter / 2.0, votexDiameter / 2.0);
    float r = length(textureCoordinateHalf);
    float factor = atan(textureCoordinateHalf.y, textureCoordinateHalf.x) + radians(angle) * 2.0 * (1.0 - (r / votexRadius) * (r / votexRadius));
    
    // 在旋渦內的圖像進行旋轉
    if (r < votexRadius){
        textureCoordinate = votexDiameter * 0.5 + r * vec2(cos(factor), sin(factor));
    }
    
    st = textureCoordinate / votexDiameter;
    vec3 irgb = texture2D(sampler, st).rgb;
    
    
    
    gl_FragColor = vec4(irgb, 1.0);
}
複製代碼

馬賽克濾鏡

提及馬賽克咱們都知道且應用的很是普遍,擋住了許多同窗的幻想和渴望,哈哈,國家爲了保護我的隱私和避免一些血腥的場景產生影響都會使用馬賽克。馬賽克不止咱們常見的那種,還有少見的蜂型馬賽克和三角形馬賽克。下面是三種馬賽克的實例。

通常馬賽克

通常馬賽克的叫法是我取的名字,爲了區分後二者馬賽克,別見怪。通常馬賽克的原理很是簡單,就是必定區域內的顏色爲同一種顏色,這樣就隱藏了圖片的細節,因此就達到了遮掩的效果。相信看到這裏的同窗就該知道去除馬賽克不靠譜了,就別惦記着了。用到了floor函數也是馬賽克的關鍵,相信聰明的同窗應該知道啥意思和用途了吧。

precision highp float;
varying lowp vec2 textureCoord;
uniform sampler2D sampler;

vec2 imageSize = vec2(400.0, 400.0); // 圖片大小
vec2 mosaicSize = vec2(8.0, 8.0);    // 馬賽克大小

void main(){
    // 計算圖像實際位置
    vec2 position = vec2(textureCoord.x * imageSize.x, textureCoord.y * imageSize.y);
    // 計算一個小馬賽克的位置並使用了floor函數,該函數是馬賽克的重要關鍵
    vec2 mosaicPosition = vec2(floor(position.x / mosaicSize.x) * mosaicSize.x, floor(position.y / mosaicSize.y) * mosaicSize.y);
    // 換算紋理座標
    vec2 texPosition = vec2(mosaicPosition.x / imageSize.x, mosaicPosition.y / imageSize.y);
    vec4 color = texture2D(sampler, texPosition);
    
    gl_FragColor = color;
}
複製代碼

蜂型馬賽克

蜂型馬賽克我只見過一兩次,第一次見得時候以爲很不錯,至少讓我看馬賽克也舒服點。可是蜂型馬賽克有點複雜。咱們在寬高比爲3:√3的矩形中看這些正六邊形。只要這些每一個六邊形的顏色一致就會變成蜂型馬賽克,也就是相似於下降了圖片的分辨率。

咱們知道這些後就須要對矩形內的點進行計算後使用不一樣的顏色進行着色就能實現。仔細看能發現是有規律的,有兩種矩形,再根據當前點離哪一個六邊形的中心點近就使用該矩形的中心點顏色進行渲染。

也就是說有左上右下、左下右上這兩種狀況,而後再根據座標來進行計算

precision highp float;
varying lowp vec2 textureCoord;
uniform sampler2D sampler;

// 馬賽克大小
const float mosaicSize = 0.03;

void main(){
    
    float length = mosaicSize;
    // 矩形長度
    float TX = 3.0 / 2.0;
    // 矩形高度
    float TY = sqrt(3.0) / 2.0;
    
    float x = textureCoord.x;
    float y = textureCoord.y;
    // 計算出當前矩形在豎向的第幾個
    int wx = int(x / TX / length);
    // 計算出當前矩形在橫向的第幾個
    int wy = int(y / TY / length);
    // 用於記錄
    vec2 v1, v2, vn;
    
    // 判斷是否爲偶數行。不能使用取餘來計算會出現錯誤,當前的做用相似於: wx % 2 == 0
    if (wy / 2 * 2 == wy) {
        if (wx / 2 * 2 == wx){
            // 左下和右上
            v1 = vec2(length * TX * float(wx), length * TY * float(wy));
            v2 = vec2(length * TX * float(wx + 1), length * TY * float(wy + 1));
        } else {
            // 左上和右下
            v1 = vec2(length * TX * float(wx), length * TY * float(wy + 1));
            v2 = vec2(length * TX * float(wx + 1), length * TY * float(wy));
        }
    }
    else {
        if (wx / 2 * 2 == wx){
            // 左上和右下
            v1 = vec2(length * TX * float(wx), length * TY * float(wy + 1));
            v2 = vec2(length * TX * float(wx + 1), length * TY * float(wy));
        } else {
            // 左下和右上
            v1 = vec2(length * TX * float(wx), length * TY * float(wy));
            v2 = vec2(length * TX * float(wx + 1), length * TY * 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(sampler, vn);
    
    gl_FragColor = color;
}
複製代碼

三角形馬賽克

三角形馬賽克在上面的蜂型馬賽克在進一步分解,一個正六邊形裏分解成六個三角形,而後在進行計算顏色值。

precision highp float;
varying lowp vec2 textureCoord;
uniform sampler2D sampler;

const float mosaicSize = 0.03;
const float PI6 = 0.62831852;

void main(){
    
    float length = mosaicSize;
    float TX = 3.0 / 2.0;
    float TY = sqrt(3.0) / 2.0;
    
    float x = textureCoord.x;
    float y = textureCoord.y;
    
    int wx = int(x / TX / length);
    int wy = int(y / TY / length);
    
    vec2 v1, v2, vn;
    
    if (wx / 2 * 2 == wx) {
        if (wy / 2 * 2 == wy){
            v1 = vec2(length * TX * float(wx + 1), length * TY * float(wy));
            v2 = vec2(length * TX * float(wx), length * TY * float(wy + 1));
        } else {
            v1 = vec2(length * TX * float(wx + 1), length * TY * float(wy + 1));
            v2 = vec2(length * TX * float(wx), length * TY * float(wy));
        }
    }
    else {
        if (wy / 2 * 2 == wy){
            v1 = vec2(length * TX * float(wx + 1), length * TY * float(wy + 1));
            v2 = vec2(length * TX * float(wx), length * TY * float(wy));
        } else {
            v1 = vec2(length * TX * float(wx + 1), length * TY * float(wy));
            v2 = vec2(length * TX * float(wx), length * TY * 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 midColor = texture2D(sampler, vn);
    
    float a = atan((x - vn.x) / (y - vn.y));
    vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TY / 2.0);
    vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TY / 2.0);
    vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TY / 2.0);
    vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TY / 2.0);
    vec2 area5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TY / 2.0);
    vec2 area6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TY / 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(sampler, vn);
    
    gl_FragColor = color;
}
複製代碼

總結

馬賽克濾鏡利用的就是一塊區域內展現一種顏色,這樣就隱藏了圖片中細節圖像,達到馬賽克的效果。同時文中也說過,知道怎麼作馬賽克,就別想着去馬賽克了。有興趣的同窗能夠再這裏獲取demo: 傳送門, 若是對你有幫助,幫忙點個star✨,謝謝。

相關文章
相關標籤/搜索