OpenGL ES入門:濾鏡篇 - 縮放、靈魂出竅、抖動等

系列推薦文章(基礎篇):
OpenGL/OpenGL ES入門:圖形API以及專業名詞解析
OpenGL/OpenGL ES入門:渲染流程以及固定存儲着色器
OpenGL/OpenGL ES入門:圖像渲染實現以及渲染問題
OpenGL/OpenGL ES入門:基礎變換 - 初識向量/矩陣
OpenGL/OpenGL ES入門:紋理初探 - 經常使用API解析
OpenGL/OpenGL ES入門: 紋理應用 - 紋理座標及案例解析(金字塔)
OpenGL/OpenGL ES入門: 頂點着色器與片元着色器(OpenGL過渡OpenGL ES)
OpenGL/OpenGL ES入門: GLKit以及API簡介
OpenGL/OpenGL ES入門: GLKit使用以及案例
OpenGL/OpenGL ES入門: 使用OpenGL ES 渲染圖片
OpenGL/OpenGL ES入門:iOS紋理翻轉策略解析
OpenGL ES入門: 渲染金字塔 - 顏色、紋理、紋理與顏色混合填充以及GLKit實現
OpenGL ES入門: 濾鏡篇 - 分屏濾鏡
OpenGL ES入門:濾鏡篇 - 漩渦、馬賽克bash

相對上一篇OpenGL ES入門:濾鏡篇 - 漩渦、馬賽克的濾鏡來講,這篇文章中的濾鏡的實現要簡單不少,由於沒有了那麼多要計算的,並且效果也比較酷炫一點,進入正題。post

縮放濾鏡

效果圖:ui

頂點着色器代碼:spa

// 頂點座標
attribute vec4 Position;
// 紋理座標
attribute vec2 TextureCoords;
// 紋理座標
varying vec2 TextureCoordsVarying;
// 時間撮(及時更新)
uniform float Time;
const float PI = 3.1415926;

void main (void) {
   
    // 一次縮放效果時長 0.6
    float duration = 0.6;
    // 最大縮放幅度
    float maxAmplitude = 0.3;
    
    // 表示時間週期.範圍[0.0~0.6];
    float time = mod(Time, duration);
    
    // amplitude [1.0,1.3]
    float amplitude = 1.0 + maxAmplitude * abs(sin(time * (PI / duration)));
    
    // 頂點座標x/y 分別乘以放大係數[1.0,1.3]
    gl_Position = vec4(Position.x * amplitude, Position.y * amplitude, Position.zw);
   
    // 紋理座標
    TextureCoordsVarying = TextureCoords;
}
複製代碼

前面兩篇文章的濾鏡都是經過自定義片元着色器來實現的,而這裏使用的是頂點着色器。 實現的原理就是經過頂點座標和紋理座標的映射關係完成。3d

上面代碼中,經過uniform傳入一個時間Time,來改變放大係數。code

靈魂出竅濾鏡

效果圖:orm

片元着色器代碼:cdn

precision highp float;
// 紋理採樣器
uniform sampler2D Texture;
// 紋理座標
varying vec2 TextureCoordsVarying;
// 時間撮
uniform float Time;

void main (void) {
    // 一次靈魂出竅效果的時長 0.7
    float duration = 0.7;
    // 透明度上限
    float maxAlpha = 0.4;
    // 放大圖片上限
    float maxScale = 1.8;
    
    // 進度值[0,1]
    float progress = mod(Time, duration) / duration; // 0~1
    // 透明度[0,0.4]
    float alpha = maxAlpha * (1.0 - progress);
    // 縮放比例[1.0,1.8]
    float scale = 1.0 + (maxScale - 1.0) * progress;
    
    // 放大紋理座標
    // 根據放大比例,獲得放大紋理座標 [0,0],[0,1],[1,1],[1,0]
    float weakX = 0.5 + (TextureCoordsVarying.x - 0.5) / scale;
    float weakY = 0.5 + (TextureCoordsVarying.y - 0.5) / scale;
    // 放大紋理座標
    vec2 weakTextureCoords = vec2(weakX, weakY);
    
    // 獲取對應放大紋理座標下的紋素(顏色值rgba)
    vec4 weakMask = texture2D(Texture, weakTextureCoords);
   
    // 原始的紋理座標下的紋素(顏色值rgba)
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    
    // 顏色混合 默認顏色混合方程式 = mask * (1.0-alpha) + weakMask * alpha;
    gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;
}
複製代碼

解析:
靈魂出竅的效果,是經過兩個圖層疊加實現的,下面那層顯示原圖,上面的那層隨着時間的推進,會逐漸放大且不透明度逐漸下降,運用到了上面的放大濾鏡效果。這裏使用片元着色器實現。blog

抖動濾鏡

效果圖:圖片

片元着色器代碼:

precision highp float;
// 紋理
uniform sampler2D Texture;
// 紋理座標
varying vec2 TextureCoordsVarying;
// 時間撮
uniform float Time;

void main (void) {
    // 一次抖動濾鏡的時長 0.7
    float duration = 0.7;
    // 放大圖片上限
    float maxScale = 1.1;
    // 顏色偏移步長
    float offset = 0.02;
    
    // 進度[0,1]
    float progress = mod(Time, duration) / duration; // 0~1
    // 顏色偏移值範圍[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;
    
    // 獲取3組顏色rgb
    // 原始顏色+offsetCoords
    vec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);
    // 原始顏色-offsetCoords
    vec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords);
    // 原始顏色
    vec4 mask = texture2D(Texture, ScaleTextureCoords);
    
    // 從3組來獲取顏色:
    // maskR.r,mask.g,maskB.b 注意這3種顏色取值能夠打亂或者隨意發揮.不必定寫死.只是效果會有不同.你們能夠試試.
    // mask.a 獲取原圖的透明度
    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}
複製代碼

實現原理也很簡單:顏色偏移 + 微弱的放大效果

閃白濾鏡

效果圖:

片元着色器代碼:

precision highp float;
// 紋理採樣器
uniform sampler2D Texture;
// 紋理座標
varying vec2 TextureCoordsVarying;
// 時間撮
uniform float Time;

void main (void) {
    // 一次閃白濾鏡的時長 0.6
    float duration = 0.6;
    // 表示時間週期[0.0,0.6]
    float time = mod(Time, duration);
    // 白色顏色遮罩層
    vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0);
    // 振幅: (0.0,1.0)
    float amplitude = abs(sin(time * (PI / duration)));
    // 紋理座標對應的紋素(RGBA)
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    
    // 利用混合方程式; 白色圖層 + 原始紋理圖片顏色 來進行混合
    gl_FragColor = mask * (1.0 - amplitude) + whiteMask * amplitude;
}
複製代碼

實現原理:添加白色圖層,白色圖層的透明度隨着時間變化

毛刺濾鏡

效果圖:

片元着色器代碼:

precision highp float;
// 紋理
uniform sampler2D Texture;
// 紋理座標
varying vec2 TextureCoordsVarying;
// 時間撮
uniform float Time;
// 隨機數
float rand(float n) {
    //fract(x),返回x的小數部分數據
    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,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; // -1~1
    
    // 是否要作偏移.
    bool needOffset = abs(jitter) < maxJitter * amplitude;
    
    // 獲取紋理X值.根據needOffset,來計算它X撕裂.
    // needOffset = YES,撕裂較大;
    // needOffset = NO,撕裂較小.
    float textureX = TextureCoordsVarying.x + (needOffset ? jitter : (jitter * amplitude * 0.006));
    
    // 撕裂後的紋理座標x,y
    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);
}
複製代碼

解析:
讓每一行/列像素隨機偏移-1~1的距離,這裏設定一個閾值,繁殖整個畫面都偏移,致使看不出原圖的樣子,當偏移小於這個閾值才進行偏移,超過這個閾值則乘上一個縮小系數。 最終的呈現效果便如上面效果圖所示。

這幾種濾鏡比較簡單,能夠經過uniform傳值,來控制上面的效果,有興趣的小夥伴可自行嘗試

相關文章
相關標籤/搜索