OpenGLES(七)使用GLSL实现滤镜效果之动态滤镜

普通图片加载的顶点着色器和片元着色器代码如下:
顶点着色器:

//顶点坐标
attribute vec4 Position;
//纹理坐标
attribute vec2 TextureCoords;
//需要传入片元着色器的纹理坐标
varying vec2 TextureCoordsVarying;

void main (void) {
    gl_Position = Position;
    TextureCoordsVarying = TextureCoords;
}

片元着色器:

//声明高精度float
precision highp float;
//纹理
uniform sampler2D Texture;
//纹理坐标
varying vec2 TextureCoordsVarying;

void main (void) {
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    gl_FragColor = vec4(mask.rgb, 1.0);
}

1. 缩放效果

在这里插入图片描述

分析:图片有一个放大的过程,然后再回弹,那么就可以通过修改顶点坐标和纹理坐标的对应关系来实现。
修改顶点坐标和纹理坐标,既可以在顶点着色器实现,也可以在片元着色器上实现,此次效果在顶点着色器上实现。
顶点着色器:

//顶点坐标
 attribute vec4 Position; 
//纹理坐标
 attribute vec2 TextureCoords; 
 //纹理坐标 
 varying vec2 TextureCoordsVarying; 
 //时间撮(及时更新)
  uniform float Time;
   //PI 
   const float PI = 3.1415926; 
   void main (void) {
    //⼀次缩放效果时⻓ = 0.6ms 
    float duration = 0.6; 
    //最⼤缩放幅度 
    float maxAmplitude = 0.3; 
    //表示传⼊的时间周期.即time的范围被控制在[0.0~0.6]; //mod(a,b),求模运算. a%b
 float time = mod(Time, duration); 
 //amplitude 表示振幅,引⼊ PI 的⽬的是为了使⽤ sin 函数,将 amplitude 的范围控制在 1.0 ~ 1.3 之间,并随着时间变化 
 float amplitude = 1.0 + maxAmplitude * abs(sin(time * (PI / duration))); 
 //放⼤关键: 将顶点坐标的 x 和 y 分别乘上⼀个放⼤系数,在纹理坐标不变的情况下,就达到了拉伸的 效果。//x,y 放⼤; z和w保存不变 
 gl_Position = vec4(Position.x * amplitude, Position.y * amplitude, Position.zw);
  纹理坐标传递给TextureCoordsVarying 
  TextureCoordsVarying = TextureCoords;

2.灵魂出窍效果

在这里插入图片描述

分析:由图中可以发现此效果有多个图层,最下面的图层不动,上面的图层随着时间的变化变大,并且透明度变低直至透明。
此效果是由多个图层构成,那么就需要颜色混合,所以此效果需要在片元着色器中实现,顶点着色器不变。
片元着色器:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

void main (void) {
	//动画效果时长
    float duration = 0.7;
    //最大透明度
    float maxAlpha = 0.4;
    //放大最大的倍数
    float maxScale = 1.8;
    
    //当前时间的进度 0-1
    float progress = mod(Time, duration) / duration; 
    //透明度的进度 0.4 - 0
    float alpha = maxAlpha * (1.0 - progress);
    //放大倍数的进度 1 - 1.8
    float scale = 1.0 + (maxScale - 1.0) * progress;
    
    //放大后的x值 0.5是中心点,中心点是不变的
    float weakX = 0.5 + (TextureCoordsVarying.x - 0.5) / scale;
    //放大后的x值 0.5是中心点,中心点是不变的
    float weakY = 0.5 + (TextureCoordsVarying.y - 0.5) / scale;
    //放大后的纹理坐标
    vec2 weakTextureCoords = vec2(weakX, weakY);
    //放大后的文素图层
    vec4 weakMask = texture2D(Texture, weakTextureCoords);
    //正常的文素图层
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    //混合
    gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;
}

3.抖动效果

在这里插入图片描述

分析:图层变大,并且颜色发生了偏移,然后所以的再变回原来的效果。
此效果修改片元着色器:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

void main (void) {
//抖动时长
    float duration = 0.7;
    //放大上限
    float maxScale = 1.1;
    //颜色偏移步长
    float offset = 0.02;
    //当前时间的进度 0-1
    float progress = mod(Time, duration) / duration; // 0~1
    //颜色偏移的进度
    vec2 offsetCoords = vec2(offset, offset) * progress;
    //缩放的进度
    float scale = 1.0 + (maxScale - 1.0) * progress;
    
    //放大后的纹理坐标
    vec2 ScaleTextureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;
    //R偏移的纹素
    vec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);
     //B偏移的纹素
    vec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords);
     //放大后的纹素
    vec4 mask = texture2D(Texture, ScaleTextureCoords);
    
    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
}

4.闪白效果

分析:在原图层上,添加一个白色图层,并根据时间戳,透明度发生变化。
片元着色器:
在这里插入图片描述

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;

void main (void) {
    float duration = 0.6;
    
    float time = mod(Time, duration);
    //白色图层
    vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0);
    float amplitude = abs(sin(time * (PI / duration)));
    
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    //混合
    gl_FragColor = mask * (1.0 - amplitude) + whiteMask * amplitude;
}

5.毛刺效果

在这里插入图片描述

分析:图片有一种撕裂的感觉,并且颜色发生偏移。
我们让每⼀⾏像素随机偏移 -1 ~ 1 的距离(这⾥的 -1 ~ 1 是对于纹理坐标来说的),但是如果整个画⾯都偏移⽐较⼤的值,那我们可能都看不出原来图像的样⼦。所以我们的逻辑是,设定⼀个阈值,⼩于这个阈值才进⾏偏移,超过这个阈值则乘上⼀个缩⼩系数。

则最终呈现的效果是:绝⼤部分的⾏都会进⾏微⼩的偏移,只有少量的⾏会进⾏较⼤偏移。
片元着色器:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;
//生成一个随机数方法
float rand(float n) {
//fract(x),返回x的⼩数部分 返回: sin(n) * 43758.5453123
    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;
    //表示将传⼊的时间转换到⼀个周期内,即 time 的范围是 0 ~ 0.6
    float time = mod(Time, duration * 2.0);
    //amplitude 表示振幅,引⼊ PI 的⽬的是为了使⽤ sin 函数,将 amplitude 的范围控制在 1.0 ~ 1.3 之间,并随着时间变化
    float amplitude = max(sin(time * (PI / duration)), 0.0);
    // -1~1 像素随机偏移范围(-1,1)
    float jitter = rand(TextureCoordsVarying.y) * 2.0 - 1.0; //判断是否需要偏移,如果jtter范围< 最⼤抖动*振幅
    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);
}

6.幻觉效果

在这里插入图片描述

还在分析中,先附上代码,稍后补充上思路。
片元着色器:

precision highp float;

uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

uniform float Time;

const float PI = 3.1415926;
const float duration = 2.0;

vec4 getMask(float time, vec2 textureCoords, float padding) {
   
    vec2 translation = vec2(sin(time * (PI * 2.0 / duration)),
                            cos(time * (PI * 2.0 / duration)));
    
    vec2 translationTextureCoords = textureCoords + padding * translation;
    vec4 mask = texture2D(Texture, translationTextureCoords);
    
    return mask;
}

float maskAlphaProgress(float currentTime, float hideTime, float startTime) {
    float time = mod(duration + currentTime - startTime, duration);
    return min(time, hideTime);
}

void main (void) {
    float time = mod(Time, duration);
    float scale = 1.2;
    float padding = 0.5 * (1.0 - 1.0 / scale);
    vec2 textureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;
    
    float hideTime = 0.9;
    float timeGap = 0.2;
    
    float maxAlphaR = 0.5; // max R
    float maxAlphaG = 0.05; // max G
    float maxAlphaB = 0.05; // max B
    
    vec4 mask = getMask(time, textureCoords, padding);
    float alphaR = 1.0; // R
    float alphaG = 1.0; // G
    float alphaB = 1.0; // B
    
    vec4 resultMask = vec4(0, 0, 0, 0);
    
    for (float f = 0.0; f < duration; f += timeGap) {
        float tmpTime = f;
        vec4 tmpMask = getMask(tmpTime, textureCoords, padding);
        
        //
        float tmpAlphaR = maxAlphaR - maxAlphaR * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
        float tmpAlphaG = maxAlphaG - maxAlphaG * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
        float tmpAlphaB = maxAlphaB - maxAlphaB * maskAlphaProgress(time, hideTime, tmpTime) / hideTime;
     
        resultMask += vec4(tmpMask.r * tmpAlphaR,
                           tmpMask.g * tmpAlphaG,
                           tmpMask.b * tmpAlphaB,
                           1.0);
        alphaR -= tmpAlphaR;
        alphaG -= tmpAlphaG;
        alphaB -= tmpAlphaB;
    }
    resultMask += vec4(mask.r * alphaR, mask.g * alphaG, mask.b * alphaB, 1.0);

    gl_FragColor = resultMask;
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值