OpenGL 12.2 - 案例:動態濾鏡效果


最終實現濾鏡效果:

詳細Demo文章見底部

一、縮放

1)實現思路:修改頂點坐標和紋理坐標的對應關系。紋理坐標不變的情況下,對頂點坐標進行放大.

縮放過程:

1、設置一次縮放效果的時長 duration 0.6

2、設置最大振幅 maxAmplitude 0.3 --> 放大程度 [1 ~ 1.3]

3、將時間控制在 縮放時長的范圍內變動 --> 通過mod(Time, duration),計算當前時間Time 所處在[0 ~ 0.6]的時間位置 time

4、計算當前振幅 amplitude --> 通過 sin() 函數 --> sin()值的范圍是[-1 ~ 1],取絕對值得范圍 [0 ~ 1] --> maxAmplitude *abs( sin(time*(PI/duration))) -->

例:當前時間 time = 0.3, sin(0.3*PI/0.6) = sin(PI/2) = 1, amplitude = 0.3*1 = 0.3

5、對頂點坐標的 x, y 進行放大

2)頂點着色器代碼

 1 attribute vec4 Position;// 頂點坐標
 2 attribute vec2 TextureCoords;// 紋理坐標
 3 varying vec2 TextureCoordsVarying;// 紋理坐標 傳入片元着色器
 4 
 5 uniform float Time;// 傳入的時間戳
 6 
 7 const float PI = 3.1415926;
 8 
 9 void main (void) {
10     
11     // 一次縮放的效果時長
12     float duration = 0.6;
13     // 最大發達幅度 - 振幅
14     float maxAmplitude = 0.3;
15     // 傳入的時間周期控制在 0~0.6,  mod 求模: mod(0.9,0.6)=0.3 
16     float time = mod(Time, duration);
17     // 求振幅 - sin值范圍-1~1, abs(sin())范圍0~1,* 0.3 即所要范圍 0~0.3
18     float amplitude = 1.0 + maxAmplitude * abs(sin(time * (PI / duration)));
19     // 將頂點坐標的 x, y 分別乘放大系數,紋理坐標不變
20     gl_Position = vec4(Position.x * amplitude, Position.y * amplitude, Position.zw);
21 
22     TextureCoordsVarying = TextureCoords;
23 }

二、靈魂出竅

1)實現思路:兩個圖層疊加,使上面那層圖層隨時間推移放大,並降低不透明度。通過片元着色器完成。

--> 圖層的放大原理:頂點坐標不變,紋理坐標取值時向中心位置偏差

縮放消失過程

坐標變換,不同位置時:

 

1、設置每次動畫時長 duration 0.7,范圍 [0 ~ 0.7];

最大透明度 maxAlpha 0.4;

最大放大比例 maxScale 1.8;

2、當前放大的進度 progress = mod(Time, duration) / duration -->  0~0.7  / 0.7 ==> 0 ~ 1

3、設置當前的透明度 alpha = maxAlpha * (1.0 - progress) --> 0~0.4

4、計算縮放倍數 scale = 1.0 + (maxScale - 1.0) * progress --> 1.0 + 0.8 *[0~1] --> 1~1.8

5、上面圖層的縮放處理, x,y值 同理: 0.5 + (TextureCoordsVarying.x - 0.5) / scale --> 原理如上圖 --> 得到縮放后的紋理坐標

6、拿到原紋理坐標 和 縮放后紋理坐標對應的紋素:texture2D()

7、兩個圖層進行混合 混合方程式:mask * (1 - alpha) + weakMask * alpha --> OpenGL 顏色混合

2)片元着色器代碼

 1 precision highp float;
 2 
 3 uniform sampler2D Texture;
 4 varying vec2 TextureCoordsVarying;
 5 
 6 uniform float Time;
 7 
 8 void main (void) {
 9     float duration = 0.7;// 一次動效時長
10     float maxAlpha = 0.4;// 最大透明度
11     float maxScale = 1.8;// 縮放最大系數
12     
13     float progress = mod(Time, duration) / duration;// mod(Time, duration):0~0.7 --> 0~1
14     float alpha = maxAlpha * (1.0 - progress);// 計算透明度
15     float scale = 1.0 + (maxScale - 1.0) * progress;// 計算縮放倍數
16     
17     float weakX = 0.5 + (TextureCoordsVarying.x - 0.5) / scale;// 紋理坐標值縮放
18     float weakY = 0.5 + (TextureCoordsVarying.y - 0.5) / scale;
19     vec2 weakTextureCoords = vec2(weakX, weakY);// 縮放后紋理坐標
20     // 縮放后紋理坐標對應的紋素
21     vec4 weakMask = texture2D(Texture, weakTextureCoords);
22     // 原紋理坐標對應紋素
23     vec4 mask = texture2D(Texture, TextureCoordsVarying);
24     // 混合2個圖層 
25     gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;// mix
26 }

三、抖動

1)實現原理:顏色偏移 + 放大效果

1、放大原理同 “二、靈魂出竅”

2、紋理左邊偏移也同理:

3、對 紅藍 RB 兩個顏色值進行偏移:maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);

2)片段着色器代碼

 1 precision highp float;
 2 
 3 uniform sampler2D Texture;
 4 varying vec2 TextureCoordsVarying;
 5 
 6 uniform float Time;
 7 
 8 void main (void) {
 9     float duration = 0.7;
10     float maxScale = 1.1;
11     float offset = 0.02;
12     
13     float progress = mod(Time, duration) / duration; // 0~1
14     vec2 offsetCoords = vec2(offset, offset) * progress;// 偏移量
15     float scale = 1.0 + (maxScale - 1.0) * progress;// 縮放
16     
17     vec2 ScaleTextureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;// 紋理坐標縮放 - 向量的加減 例:向量 AB, A+B=(Ax+Bx, Ay+By)
18     
19     vec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);// red
20     vec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords);// blue
21     vec4 mask = texture2D(Texture, ScaleTextureCoords);// 原紋素
22     
23     gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);// RGB 中 RB 取b偏移后的值
24 }

四、閃白

1)實現原理:添加一層透明度變化的白色圖層進行混合

1、動畫效果時長 0.6

2、白色圖層透明度范圍 0 ~ 1

2)片元着色器代碼

 1 precision highp float;
 2 
 3 uniform sampler2D Texture;
 4 varying vec2 TextureCoordsVarying;
 5 
 6 uniform float Time;
 7 
 8 const float PI = 3.1415926;
 9 
10 void main (void) {
11     float duration = 0.6;// 動畫時長
12     
13     float time = mod(Time, duration);// 當前時間 0 ~ 0.6
14     
15     vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0);// 白色圖層
16     float amplitude = abs(sin(time * (PI / duration)));// 透明度
17     // 紋理坐標對應紋素
18     vec4 mask = texture2D(Texture, TextureCoordsVarying);
19     // mix
20     gl_FragColor = mask * (1.0 - amplitude) + whiteMask * amplitude;
21 }

五、毛刺 - 噪聲效果

1)實現原理:撕裂(x方向) + 顏色偏移

1、設置最大抖動值 maxJitter 0.06

一次濾鏡動畫時長 duration 0.3

顏色偏移colorROffset 0.01; colorBOffset -0.025

2、振幅 amplitude [1.0 ~ 1.3] 

3、偏移隨機值 jitter [-1 ~ 1]

4、判斷是否要偏移 jitter < maxJitter * amplitude

5、偏移紋理坐標的 X: x + jitter

6、設置顏色偏移

2)片元着色器代碼

 1 precision highp float;
 2 
 3 uniform sampler2D Texture;
 4 varying vec2 TextureCoordsVarying;
 5 
 6 uniform float Time;
 7 
 8 const float PI = 3.1415926;
 9 
10 // 獲取一個隨機值
11 float rand(float n) {
12     return fract(sin(n) * 43758.5453123);
13 }
14 
15 void main (void) {
16     float maxJitter = 0.06;// 最大抖動
17     float duration = 0.3;// 一次動畫時長
18     float colorROffset = 0.01;// 紅色偏移
19     float colorBOffset = -0.025;// 藍色偏移
20     
21     float time = mod(Time, duration * 2.0);// 時間周期 0 ~ 0.6
22     float amplitude = max(sin(time * (PI / duration)), 0.0);// 振幅
23     
24     float 0 = rand(TextureCoordsVarying.y) * 2.0 - 1.0; // 向所偏移隨機值 -1 ~ 1
25     bool needOffset = abs(jitter) < maxJitter * amplitude;// 是否需要偏移 --> yes:撕裂大  no:撕裂小
26     
27     float textureX = TextureCoordsVarying.x + (needOffset ? jitter : (jitter * amplitude * 0.006));// 紋理坐標的 X 撕裂
28     
29     // 撕裂后紋理坐標
30     vec2 textureCoords = vec2(textureX, TextureCoordsVarying.y);
31     
32     // 顏色偏移
33     vec4 mask = texture2D(Texture, textureCoords);// 
34     vec4 maskR = texture2D(Texture, textureCoords + vec2(colorROffset * amplitude, 0.0));
35     vec4 maskB = texture2D(Texture, textureCoords + vec2(colorBOffset * amplitude, 0.0));
36     
37     // 顏色 RB 
38     gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
39 }

六、幻覺 - 綜合

1)實現原理:殘影效果 + 顏色偏移

這里:

殘影效果:在移動的過程中,每經過⼀段時間間隔,根據當前的位置去創建一個新圖層,並且新層的不透明度隨着時間逐漸減弱。於是在⼀個移動周期內,可以看到很多透明度不同的圖層疊加在一起,從⽽形成殘影的效果。殘影,讓圖⽚隨着時間做圓周運動。

顏色偏移:物體移動的過程是藍色在前面,紅⾊在后。整個過程可以理解成:在移動的過程中,每間隔一段時間,遺失了一部分紅色通道的值在原來的位置,並且這部分紅⾊通道的值,隨着時間偏移,會逐漸恢復。

2)片元着色器代碼

 1 precision highp float;
 2 
 3 uniform sampler2D Texture;
 4 varying vec2 TextureCoordsVarying;
 5 
 6 uniform float Time;
 7 
 8 const float PI = 3.1415926;
 9 const float duration = 2.0;
10 
11 // 自定義函數 - 計算在某個時刻圖片的具體位置,通過它可以每經過一個時間段,生層一個新的圖層
12 vec4 getMask(float time, vec2 textureCoords, float padding) {
13    // 圓周坐標
14     vec2 translation = vec2(sin(time * (PI * 2.0 / duration)),
15                             cos(time * (PI * 2.0 / duration)));
16     // 紋理坐標 = 紋理坐標 + 偏移量 * 圓周坐標
17     vec2 translationTextureCoords = textureCoords + padding * translation;
18     // 新圖層的坐標
19     vec4 mask = texture2D(Texture, translationTextureCoords);
20     
21     return mask;
22 }
23 
24 // 計算某個時刻創建的層在當前的 透明度
25 float maskAlphaProgress(float currentTime, float hideTime, float startTime) {
26     float time = mod(duration + currentTime - startTime, duration);
27     return min(time, hideTime);
28 }
29 
30 void main (void) {
31 
32     float time = mod(Time, duration);// 時間 [0 ~ 2.0]
33     float scale = 1.2;// 放大倍數 1.2
34     float padding = 0.5 * (1.0 - 1.0 / scale);// 偏移量
35     vec2 textureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;// 放大后的紋理坐標
36     
37     float hideTime = 0.9;// 隱藏時間
38     float timeGap = 0.2;// 時間間隔
39     
40     // 只保留了紅色的透明通道值,我們這里設置幻影效果紅色,可設其他色值
41     // 新圖層的最大 RGB
42     float maxAlphaR = 0.5; // max R
43     float maxAlphaG = 0.05; // max G
44     float maxAlphaB = 0.05; // max B
45 
46     // 獲得新的圖層坐標
47     vec4 mask = getMask(time, textureCoords, padding);
48     float alphaR = 1.0; // R
49     float alphaG = 1.0; // G
50     float alphaB = 1.0; // B
51 
52     // 最終圖層顏色
53     vec4 resultMask = vec4(0, 0, 0, 0);
54     
55     // 循環 多個圖層
56     for (float f = 0.0; f < duration; f += timeGap) {
57 
58         float tmpTime = f;
59         // 獲取 0 ~ 2 秒內的, 運動后的 紋理坐標
60         vec4 tmpMask = getMask(tmpTime, textureCoords, padding);
61         
62         // 某時刻創建的層,在當前的 RGB 透明度
63         float tmpAlphaR = maxAlphaR - maxAlphaR * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
64         float tmpAlphaG = maxAlphaG - maxAlphaG * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
65         float tmpAlphaB = maxAlphaB - maxAlphaB * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
66 
67         // 積累每一層 的 每個通道 乘以 透明度顏色值 
68         resultMask += vec4(tmpMask.r * tmpAlphaR,
69                            tmpMask.g * tmpAlphaG,
70                            tmpMask.b * tmpAlphaB,
71                            1.0);
72         // 透明度遞減
73         alphaR -= tmpAlphaR;
74         alphaG -= tmpAlphaG;
75         alphaB -= tmpAlphaB;
76     }
77 
78     // 最終顏色 += 紅綠藍 * 透明度
79     resultMask += vec4(mask.r * alphaR, mask.g * alphaG, mask.b * alphaB, 1.0);
80     // 填充到像素點
81     gl_FragColor = resultMask;
82 }     

Demo地址

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM