这篇文章介绍一下带有动态效果的滤镜,大家可以先了解下 分屏滤镜 和 马赛克滤镜 。
动态效果的滤镜其实就是根据当前时间的变化来进行动态的绘制,所以我们就需要添加一个计时器进行不断的绘制。控制器添加计时器代码:
// 开始一个滤镜动画
- (void)startFilerAnimation {
//1.判断displayLink 是否为空
//CADisplayLink 定时器
if (self.displayLink) {
[self.displayLink invalidate];
self.displayLink = nil;
}
//2. 设置displayLink 的方法
self.startTimeInterval = 0;
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
//3.将displayLink 添加到runloop 运行循环
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop]
forMode:NSRunLoopCommonModes];
}
添加了计时器,绘制代码需要将当前时间传给顶点着色器或者片元着色器,以便着色器程序利用当前时间动态的绘制滤镜,修改代码:
- (void)render {
//DisplayLink 的当前时间撮
if (self.startTimeInterval == 0) {
self.startTimeInterval = self.displayLink.timestamp;
}
//使用program
glUseProgram(self.programID);
//绑定buffer
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBufferID);
// 传入时间
CGFloat currentTime = self.displayLink.timestamp - self.startTimeInterval;
GLuint time = glGetUniformLocation(self.programID, "time");
glUniform1f(time, currentTime);
// 清除画布
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1, 1, 1, 1);
// 重绘
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//渲染到屏幕上
[self.currentContext presentRenderbuffer:GL_RENDERBUFFER];
}
下面我们来看下不同的动态效果如何如何修改着色器代码。
一、缩放滤镜
缩放滤镜我们这里修改的是顶点着程序的代码,只需要将顶点坐标根据时间变化来进行放大缩小。看下Scale.vsh
代码:
attribute vec4 position;
attribute vec2 texCoordinate;
varying vec2 textureCoords;
uniform float time;
const float PI = 3.1415926;
void main()
{
float duration = 0.6;
float maxAmplitude = 0.3;
float timeProgress = mod(time, duration);
float currentAmplitude = 1.0 + maxAmplitude * abs(sin(timeProgress * (PI / duration)));
gl_Position = vec4(position.x * currentAmplitude, position.y * currentAmplitude, position.zw);
textureCoords = texCoordinate;
}
-
duration
:持续时间,单次放大或缩小需要的时间。 -
maxAmplitude
:放大的最大幅度,这里写0.3意味着就是最大幅度为原图的1.0 + 0.3 = 1.3
倍。 -
timeProgress
:时间进度,mod()
的意思是取余,所以这里的范围是[0, 0.6]
。 -
currentAmplitude
:当前的放大幅度。
timeProgress * (PI / duration)
计算出来的是当前时间对应的弧度值取值范围是[0, PI]
,所以这里的sing()
值的取值范围是[0, 1]
。
abs()
取绝对值,这里可以省略。
currentAmplitude
的取值范围是`[1.0, 1.3]。 -
vec4(position.x * currentAmplitude, position.y * currentAmplitude, position.zw);
:顶点坐标只需要在x、y
方向上变化。
二、灵魂出窍效果
这种效果实际上是对两个层的叠加,并且上面的那层随着时间的推移,会逐渐放⼤大且不透明度逐渐降低。这里也⽤到了放⼤的效果,我们需要对纹理坐标进行处理。看SoulOut.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
uniform float time;
void main()
{
float duration = 0.7;
float maxAlpha = 0.4;
float maxScale = 1.8;
float timeProgress = mod(time, duration) / duration;
float currentAlpha = timeProgress * (1.0 - maxAlpha);
float currentScale = 1.0 + (maxScale - 1.0) * timeProgress;
float weakX = 0.5 + (textureCoords.x - 0.5) / currentScale;
float weakY = 0.5 + (textureCoords.y - 0.5) / currentScale;
vec2 weakTextureCoords = vec2(weakX, weakY);
vec4 weakMask = texture2D(texture, weakTextureCoords);
vec4 mask = texture2D(texture, textureCoords);
gl_FragColor = mask * (1.0 - currentAlpha) + weakMask * currentAlpha;
}
-
timeProgress
:时间进度,这里的取值范围是[0, 1]
。 -
currentAlpha
:混合图层的当前透明度,取值范围[0, 0.6]
。 -
currentScale
: 混合图层当前缩放比例,取值范围[1.0, 1.8]
。 -
weakX
:叠加图层在x方向上的放大比例。这里是以纹理坐标(0..5, 0.5)
为中心进行放大,放大倍数为[1.0, 1.8]
。 -
weakY
:混合图层在y方向上的放大比例。 -
mask * (1.0 - currentAlpha) + weakMask * currentAlpha;
叠加纹理计算公式。
三、抖动
抖动效果是对纹理进行轻微的放大缩小,然后进行纹素偏移,也是对纹理进行处理,Shake.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
uniform float time;
void main()
{
float duration = 0.7;
float maxScale = 1.1;
float textOffset = 0.02;
float timeProgress = mod(time, duration) / duration;
vec2 offsetCoorsd = vec2(textOffset, textOffset) * timeProgress;
float currentScale = 1.0 + (maxScale - 1.0) * timeProgress;
float weakX = 0.5 + (textureCoords.x - 0.5) / currentScale;
float weakY = 0.5 + (textureCoords.y - 0.5) / currentScale;
vec2 weakTextureCoords = vec2(weakX, weakY);
vec4 maskR = texture2D(texture, weakTextureCoords + offsetCoorsd);
vec4 maskB = texture2D(texture, weakTextureCoords - offsetCoorsd);
vec4 mask = texture2D(texture, textureCoords);
gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}
-
offsetCoorsd
:纹理坐标偏移量。 -
currentScale
:当前的缩放比例。 -
weakX
:纹理在x方向上的放大比例。这里是以纹理坐标(0..5, 0.5)
为中心进行放大,放大倍数为[1.0, 1.1]
。 -
weakY
:纹理在y方向上的放大比例。 -
maskR.r
:颜色分量R
对应的放大偏移的值。 -
mask.g
:颜色分量G
对应的值,该分量既没有放大也没有偏移。 -
maskR.b
:颜色分量B
对应的放大偏移的值。 -
mask.a
:颜色分量A
透明度对应的值,该分量既没有放大也没有偏移。
三、闪白
该效果是叠加白色图层 ,并且白色图层的透明度随着时间变化,需要对纹理进行操作。ShineWhite.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
uniform float time;
const float PI = 3.1415926;
void main()
{
float duration = 0.7;
float timeProgress = mod(time, duration);
vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0);
float currentAlpha = abs(sin((PI/duration) * timeProgress));
vec4 mask = texture2D(texture, textureCoords);
gl_FragColor = mask * (1.0 - currentAlpha) + whiteMask * currentAlpha;
}
-
whiteMask
:需要叠加的白色图层。 -
currentAlpha
:白色图层的当前透明度,取值范围[0, 1]
。 -
mask * (1.0 - currentAlpha) + whiteMask * currentAlpha;
:叠加图层计算方式。
四、毛刺效果
我们先来介绍下毛刺效果的具体思路:
让每一行纹素随机偏移 [-1, 1]
的距离。设想一下如果整个画面都偏移比较大的值,我们可能就看不出原图像样子。所以我们的逻辑是,设定一个阈值,⼩于这个阈值才进行偏移,超过这个阈值则乘上一个缩小系数。则最终呈现的效果是:绝大部分的行都会进行微⼩的偏移,只有少量的行会进行较大偏移。
Glitch.fsh
代码:
precision highp float;
varying vec2 textureCoords;
uniform sampler2D texture;
uniform float time;
const float PI = 3.1415926;
float rand(float n) {
return fract(sin(n) * 43758.5453123);
}
void main()
{
float maxJitter = 0.06;
float duration = 0.3;
float colorROffset = 0.01;
float colorBOffset = -0.025;
float timeProgress = mod(time, duration * 2.0);
float amplitude = max(sin(timeProgress * (PI / duration)), 0.0);//sin([0, 2PI]):
float jitter = rand(textureCoords.y) * 2.0 - 1.0; // -1~1
bool needOffset = abs(jitter) < maxJitter * amplitude;
float textureXOffset = 0.0;
if (needOffset == true) {
textureXOffset = jitter;
} else {
textureXOffset = jitter * amplitude * 0.006;
}
float textureX = textureCoords.x + textureXOffset;
vec2 textCoords = vec2(textureX, textureCoords.y);
vec4 mask = texture2D(texture, textCoords);
vec4 maskR = texture2D(texture, textCoords + vec2(colorROffset * amplitude, 0.0));
vec4 maskB = texture2D(texture, textCoords + vec2(colorBOffset * amplitude, 0.0));
gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}
-
maxJitter
:最大抖动效果,也就是我们设置的最大的偏移量。 -
duration
:持续时间。 -
colorROffset
:颜色分量R
上的偏移量。 -
colorBOffset
:颜色分量B
上的偏移量。 -
timeProgress
:时间进度,取值范围[0, 0.6]
。 -
sin()
:timeProgress * (PI / duration)
的取值范围是[0, 2PI]
,所以sin()
函数在这的取值范围是[-1, 1]
。 -
amplitude
:偏移的幅度,取值范围[0, 1]
。 -
rand()
函数:获取一个随机的小数值[0.0, 1.0)
, 所以jitter
的值为[-1, 1)
。 -
maxJitter * amplitude
:便宜的最大阙值。 -
jitter * amplitude * 0.006
:这里的0.06
就是我们随机设置的缩小系数,目的是缩小偏移量。 -
textCoords
:偏移后的纹理坐标。 -
maskR
:在偏移的基础上再进行在x
方向上一定的偏移,目的是为了设置颜色分量R
的值,使视觉效果产生颜色差异。 -
maskB
:在偏移的基础上再进行在x
方向上一定的偏移,目的是为了设置颜色分量B
的值,使视觉效果产生颜色差异。
网友评论