美文网首页
OpenGL ES 动态效果滤镜

OpenGL ES 动态效果滤镜

作者: Maji1 | 来源:发表于2020-08-17 16:56 被阅读0次

这篇文章介绍一下带有动态效果的滤镜,大家可以先了解下 分屏滤镜马赛克滤镜

动态效果的滤镜其实就是根据当前时间的变化来进行动态的绘制,所以我们就需要添加一个计时器进行不断的绘制。控制器添加计时器代码:

// 开始一个滤镜动画
- (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的值,使视觉效果产生颜色差异。

相关文章

网友评论

      本文标题:OpenGL ES 动态效果滤镜

      本文链接:https://www.haomeiwen.com/subject/kvsndktx.html