系列推薦文章(基礎篇):
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
傳值,來控制上面的效果,有興趣的小夥伴可自行嘗試