OpenGL ES 濾鏡動效——縮放、靈魂出竅、抖動、閃白、毛刺、幻覺

寫在前面,本文所展現的濾鏡效果,實現方式多種多樣,這裏僅提供一個思路參考。
複製代碼

1.縮放 Scale

先上效果圖: 思路分析:經過效果圖能夠看出,圖片的大小隨着時間的推移,呈現週期性的變化。那麼咱們能夠根據發現的這個規律,選用正弦三角函數sin來幫助咱們實現這個效果。markdown

正弦函數變化規律以下,取值在[-1,1]之間。 ide

圖片的縮放,只須要在頂點着色器裏改變一下頂點座標便可,思惟導圖以下: 函數

實現代碼:oop

//頂點座標
attribute vec4 Position;
//紋理座標
attribute vec2 TextureCoords;
//紋理座標
varying vec2 TextureCoordsVarying;
//時間戳
uniform float Time;
//π
const float PI = 3.1415926;

void main (void) {
   
    //縮放的時間週期
    float duration = 0.6;
    //最大放大倍數
    float maxAmplitude = 0.3;
    
    //Time / duration * PI   當前時間相對多少個PI
    //abs(sin(Time / duration * PI) 計算sin 並取絕對值
    //maxAmplitude * abs(sin(Time / duration * PI)) 求得當前放大係數
    float amplitude = 1.0 + maxAmplitude * abs(sin(Time / duration * PI));
    
    //將頂點座標的x y分別乘以放大係數, 在紋理座標不變的狀況下達到拉伸效果
    //xy放大 zw不變
    gl_Position = vec4(Position.x * amplitude, Position.y * amplitude, Position.zw);
    TextureCoordsVarying = TextureCoords;
}
複製代碼

2.靈魂出竅 SoulOut

效果圖: 思路分析:spa

  • 靈魂出竅效果,像是在原始圖層上疊加了放大和透明效果
  • 一樣隨着時間的推移,圖層放大、透明度下降
  • 最終的效果要將這兩個圖層混合到一塊兒

思惟導圖: 3d

實現代碼:code

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

//時間戳
uniform float Time;

void main (void) {
    //時間週期
    float duration = 0.7;
    //透明度上限
    float maxAlpha = 0.4;
    //放大上限
    float maxScale = 1.8;
    
    //進度(0-1)
    float progress = mod(Time, duration)/duration;
    //透明度(0-4)
    float alpha = maxAlpha * (1.0 - progress);
    //縮放比例
    float scale = 1.0 + (maxScale - 1.0) * progress;
    
    //放大紋理座標
    //將頂點座標對應的紋理座標的x值到紋理中點的距離,放大必定的比例. 此次咱們是改變了紋理座標, 而保持頂點座標不變, 一樣達到了拉伸的效果
    float weakX = 0.5 + (TextureCoordsVarying.x - 0.5) / scale;
    float weakY = 0.5 + (TextureCoordsVarying.y - 0.5) / scale;
    
    //獲得放大的紋理座標
    vec2 weakTextureCoords = vec2(weakX, weakY);
    
    //讀取放大後的紋理座標對應紋素的顏色值
    vec4 weakMask = texture2D(Texture, weakTextureCoords);
    
    //讀取原始的紋理座標對應紋素的顏色值
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    
    //在GLSL 實現顏色混合方程式. 默認顏色混合方程式 = mask * (1.0 - alpha) + weakMask *alpha
    //混合後的顏色 賦值給gl_FragColor
    gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;

}

複製代碼

3.抖動 Shake

效果圖: 思路分析:orm

  • 有放大的效果
  • 有顏色的偏移
  • 隨之變化而變化

思惟導圖: 代碼實現:事件

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

uniform float Time;

void main (void) {
	//一次抖動效果的時長
    float duration = 0.7;
    //放大圖片的上限
    float maxScale = 1.1;
    //顏色偏移的步長
    float offset = 0.02;
    
    //進度 0 ~ 1
    float progress = mod(Time, duration) / duration; 
    //顏色偏移值0 ~ 0.02
    vec2 offsetCoords = vec2(offset, offset) * progress;
    //縮放比例 1.0 ~ 1.1
    float scale = 1.0 + (maxScale - 1.0) * progress;
    
    //放大後的紋理座標 
    vec2 ScaleTextureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;
    
    //獲取三組顏色
    //原始顏色 + offset
    vec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);
    //原始顏色 - offset
    vec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords);
    //原始顏色
    vec4 mask = texture2D(Texture, ScaleTextureCoords);
    
    //從3組顏色中分別取出 紅色R,綠色G,藍色B,透明度A填充到內置變量gl_FragColor內
    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}

複製代碼

4.閃白 ShineWhite

效果圖: 圖片

思路分析:

  • 像是在原始圖層上疊加了白色圖層
  • 一樣隨着時間的推移,白色透明度改變
  • 將紋理顏色和白色混合

思惟導圖:

實現代碼:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;

void main (void) {
    float duration = 0.6;
    
    float time = mod(Time, duration);
    
    //定義一個白色顏色遮罩
    vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0);
    
    float amplitude = abs(sin(time * (PI / duration)));
    
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    
    gl_FragColor = mask * (1.0 - amplitude) + whiteMask * amplitude;
}

複製代碼

5.毛刺 Glitch

效果圖: 思路分析:

  • 隨着時間變化 發生變化 能夠利用sin函數
  • 發生橫向撕裂,考慮只發生x軸方向上的顏色偏移
  • 發生撕裂位置位置隨機
  • 不多部分位置偏移,考慮設置判斷閾值,閾值之內才偏移

思惟導圖: 代碼實現:

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
//時間戳
uniform float Time;
//PI常量
const float PI = 3.1415926;
//GLSL中不提供隨機生成數 須要本身實現一個
//fract(x) 返回x的小數部分
//隨機生成0-1的小數
float rand(float n) {
    return fract(sin(n) * 43758.5453123);
}

void main (void) {
	//最大抖動上限
    float maxJitter = 0.06;
    //一次毛刺效果的時長
    float duration = 0.3;
    //紅色顏色偏移
    float colorROffset = 0.01;
    //綠色顏色偏移
    float colorBOffset = -0.025;
    
    //表示將傳入的事件轉換到一個週期內,範圍是 0 ~ 0.6,抖動時長變成0.6
    float time = mod(Time, duration * 2.0);
    //振幅,隨着時間變化,範圍是[0, 1]            
    float amplitude = max(sin(time * (PI / duration)), 0.0);
    
    //像素隨機偏移範圍 [-1,1]
    float jitter = rand(TextureCoordsVarying.y) * 2.0 - 1.0; 
    //判斷是否須要偏移,若是jitter範圍 < 最大範圍*振幅
    bool needOffset = abs(jitter) < maxJitter * amplitude;
    
    //獲取紋理x座標,根據needOffset來計算它的x撕裂
    float textureX = TextureCoordsVarying.x + (needOffset ? jitter : (jitter * amplitude * 0.006));
    //獲取紋理撕裂後的紋理
    vec2 textureCoords = vec2(textureX, TextureCoordsVarying.y);
    
    //顏色偏移:獲取3組顏色
    //撕裂後的原圖顏色
    vec4 mask = texture2D(Texture, textureCoords);
     //根據撕裂計算後的紋理座標,獲取紋素
    vec4 maskR = texture2D(Texture, textureCoords + vec2(colorROffset * amplitude, 0.0));
     //根據撕裂計算後的紋理座標,獲取紋素
    vec4 maskB = texture2D(Texture, textureCoords + vec2(colorBOffset * amplitude, 0.0));
    
    //顏色主要撕裂,紅色和藍色部分,因此只調整紅色
    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}

複製代碼

6.幻覺 Vertigo

效果圖: 思路分析:

  1. 經過效果分析,實際像是有多個紅色殘影圍繞中心點爲位移,考慮使用sin三角函數
  2. 原圖放大
  3. 複製多個圖層
  4. 複製的圖層爲爲紅色
  5. 複製的紅色透明度逐漸變大 直至消失
  6. 個圖層繞中心點週期性位置偏移

思惟導圖: 代碼實現:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;
//一次幻覺效果的時長
const float duration = 2.0;

//這個函數能夠計算出,在某個時刻圖片的具體位置,經過它咱們能夠每通過一段時間,去生成一個新的圖層
//返回當前圖層像素點的顏色值
vec4 getMask(float time, vec2 textureCoords, float padding) {
   
    vec2 translation = vec2(sin(time * (PI * 2.0 / duration)),
                            cos(time * (PI * 2.0 / duration)));
    
    vec2 translationTextureCoords = textureCoords + padding * translation;
    vec4 mask = texture2D(Texture, translationTextureCoords);
    
    return mask;
}
//這個函數能夠計算出,某個時刻建立的層,在當前時刻的透明度
float maskAlphaProgress(float currentTime, float hideTime, float startTime) {
    float time = mod(duration + currentTime - startTime, duration);
    return min(time, hideTime);
}

void main (void) {
    //將傳入的時間戳轉換到一個週期內,time的範圍是 [0,2]
    float time = mod(Time, duration);
    //放大倍數
    float scale = 1.2;
    //偏移量
    float padding = 0.5 * (1.0 - 1.0 / scale);
    //放大後的紋理座標
    vec2 textureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;
    //放大後紋理座標對應紋素的顏色值
    vec4 mask = getMask(time, textureCoords, padding);
    
    //隱藏時間
    float hideTime = 0.9;
    //時間間隔
    float timeGap = 0.2;
    
    //注意:只保留了紅色的透明的通道值,由於幻覺效果殘留紅色
    //幻影殘留數據
    //max RGB alpha
    //新圖層的 R透明度
    float maxAlphaR = 0.5;
    //新圖層的 G透明度
    float maxAlphaG = 0.05;
    //新圖層的 B透明度
    float maxAlphaB = 0.05;
   
    float alphaR = 1.0; // R
    float alphaG = 1.0; // G
    float alphaB = 1.0; // B
    
     //最終圖層顏色
    vec4 resultMask = vec4(0, 0, 0, 0);
    
    for (float f = 0.0; f < duration; f += timeGap) {
        float tmpTime = f;
        //得到幻影當前時間的顏色值
        vec4 tmpMask = getMask(tmpTime, textureCoords, padding);
        
         //某個時刻建立的層,在當前時刻的紅綠藍的透明度
        float tmpAlphaR = maxAlphaR - maxAlphaR * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
        float tmpAlphaG = maxAlphaG - maxAlphaG * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
        float tmpAlphaB = maxAlphaB - maxAlphaB * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
     
        //累計每一層臨時RGB * RGB的臨時透明度
        resultMask += vec4(tmpMask.r * tmpAlphaR,
                           tmpMask.g * tmpAlphaG,
                           tmpMask.b * tmpAlphaB,
                           1.0);
                           
        //透明度遞減
        alphaR -= tmpAlphaR;
        alphaG -= tmpAlphaG;
        alphaB -= tmpAlphaB;
    }
    
     //最終顏色 += 原始紋理的RGB * 透明度
    resultMask += vec4(mask.r * alphaR, mask.g * alphaG, mask.b * alphaB, 1.0);

    //將最終顏色填充到像素點裏
    gl_FragColor = resultMask;
}

複製代碼

demo在這裏

相關文章
相關標籤/搜索