十六、利用GLSL实现动态滤镜
2020-08-16 本文已影响0人
夏天的枫_
工程是在另一文:[利用GLSL实现分屏滤镜] 基础上增加滤镜功能;核心是在着色器程序的编写。
先来喵一波
![](https://img.haomeiwen.com/i3674086/18c240bd3229ba61.gif)
缩放滤镜
缩小放大是通过改变顶点坐标和纹理坐标的对应关系来实现缩放,纹理坐标不变,对顶点坐标进行一定程度的缩小放大。定义一个动画循环,让它的顶点坐标在1-1.2倍间放大,反复循环。
//在顶点着色程序中
attribute vec4 att_position;
attribute vec2 att_textuteCoords;
varying vec2 var_textureCoords;
// 由代码传入的时间戳,记录动画
uniform float Time;
const float PI = 3.141592654;
void main()
{
// 动画总时长
float duration = 0.4;
// 最大幅度
float maxAmplitude = 0.2;
// 动画时间间隔
float timeInterval = mod(Time,duration);
// 利用余弦[-1,1]的编号,对其取绝对值则就是[0,1]的变化,则整个幅度变量就是在1~1.2之间缩放
float amplitude = 1.0 + maxAmplitude * abs(sin(timeInterval * (PI / duration)));
// 将计算好的x,y坐标赋值给顶点坐标的内建变量,zw无需变化
gl_Position = vec4(att_position.x * amplitude , att_position.y * amplitude, att_position.zw);
var_textureCoords = att_textuteCoords;
}
灵魂出窍滤镜
灵魂出窍滤镜的实现可以通过两个图层的叠加,使得处于上层的图层随着时间的衰减让其透明度不断降低,该过程中同时让其放大。
precision highp float;
uniform sampler2D un_texture;
varying vec2 var_textureCoords;
uniform float Time;
void main()
{
float duration = 0.7;
// 最大的透明度,不断降低至0
float maxAlpha = 0.4;
// 图层被放大的最大比例
float maxScale = 1.8;
// 定义一个进度来记录动画
float progress = mod(Time , duration) / duration;
float curAlpha = maxAlpha * (1.0 - progress);
// 根据进度的快慢来描述当前纹理的尺寸
float size = 1.0 + (maxScale - 1.0) * progress;
// 被放大的纹理坐标x,y
float zoomInTextureX = 0.5 + (var_textureCoords.x - 0.5) / size;
float zoomInTextureY = 0.5 + (var_textureCoords.y - 0.5) / size;
// 放大后纹理坐标
vec2 zoomInTextureCoord = vec2(zoomInTextureX,zoomInTextureY);
// 被放大后纹理颜色值
vec4 zoomInTextureCoordColor = texture2D(un_texture,zoomInTextureCoord);
// 原始的纹理颜色值
vec4 originTextureCoordColor = texture2D(un_texture,var_textureCoords);
// 将不断变化的颜色值赋值给内建变量片元颜色值
gl_FragColor = originTextureCoordColor * (1.0 - curAlpha) + zoomInTextureCoordColor * curAlpha ;
}
抖动滤镜
抖动滤镜的实现是通过图层的放大,并对其颜色值加之一定的偏移实现的。
precision highp float;
uniform sampler2D un_texture;
varying vec2 var_textureCoords;
uniform float Time;
void main()
{
float duration = 0.4;
// 颜色偏移量
float offset = 0.02;
// 放大最多1.5倍
float maxSize = 1.5;
float progress = mod(Time,duration);
// 颜色偏移值 0~0.02
vec2 offsetCoord = vec2(offset,offset) * progress;
// 尺寸在1~1.5
float size = 1.0 + (maxSize - 1.0) * progress;
// 放大后的纹理坐标
vec2 sizeTextureCoord = vec2(0.5, 0.5) + (var_textureCoords - vec2(0.5,0.5)) / size;
// 定义三组纹素
// 原始颜色 (red) + 颜色偏移值
vec4 colorTexelR = texture2D(un_texture, sizeTextureCoord + offsetCoord);
// 原始颜色 (green) + 颜色偏移值
vec4 colorTexelG = texture2D(un_texture, sizeTextureCoord - offsetCoord);
// 原始颜色
vec4 colorTexel = texture2D(un_texture, sizeTextureCoord);
// 取出三组颜色填充到片元颜色值,
gl_FragColor = vec4(colorTexelR.r , colorTexelG.g, colorTexel.b, colorTexel.a);
}
闪白滤镜
闪白滤镜的实现通过添加一个白色图层,并且让其透明度伴随时间变化。
precision highp float;
uniform sampler2D un_texture;
varying vec2 var_textureCoords;
uniform float Time;
const float PI = 3.141593653589793;
void main()
{
// 一次滤镜的动效时长
float duration = 0.4;
// 0~0.4间
float timeInterval = mod(Time,duration);
// 定义一个白色遮罩
vec4 whiteMask = vec4(1.0,1.0,1.0,1.0);
// 振幅,0.0~1.0
float amplitude = abs(sin(timeInterval * ( PI / duration)));
// 获取纹理坐标的颜色值
vec4 texelColor = texture2D(un_texture,var_textureCoords);
//利⽤混合⽅程式: 将纹理颜⾊与⽩⾊遮罩融合. 注意: ⽩⾊遮罩的透明度会随着时间变化做调整 。利用当前的透明度来计算最终的颜⾊值即可。
gl_FragColor = (1.0 - amplitude) * texelColor + amplitude * whiteMask;
}
毛刺滤镜
毛刺滤镜的实现可以让每一行的像素产生一个随机偏移,使得其纹理坐标在-1,1的随机变化。是否随机偏移,判断随机偏移量是否超过一个阈值,如果小于,就将其与一个缩小系数相乘,这样就实现了两层把控,图层并不是每一行像素都发生撕裂以及颜色偏移。
precision highp float;
uniform sampler2D un_texture;
varying vec2 var_textureCoords;
uniform float Time;
const float PI = 3.141592653589793;
// 产生一个随机数
float rand(float n){
// fract(x) - 返回x的小数部分
return fract(sin(n) * 38989.128938897);
}
void main()
{
// 最大抖动
float maxJitter = 0.06;
// 毛刺滤镜时长
float duration = 0.3;
// 红色偏移
float colorR_Offset = 0.01;
// 绿色偏移
float colorB_Offset = -0.025;
//将传⼊的时间转换到⼀个周期内, 0 ~ 0.06
float time = mod(Time , duration * 2.0);
float amplitude = max(sin(time * (PI/duration)),0.0);
// 像素随机范围 -1~1
float jitter = rand(var_textureCoords.y) * 2.0 - 1.0;
// 判断是否需要偏移,如果jitter范围< 最⼤抖动*振幅,为ture
bool needOffset = abs(jitter) < maxJitter * amplitude;
//获取纹理x 坐标,根据needOffset,来计算它的X撕裂,如果是needOffset = yes 则撕裂⼤;如果 needOffset = no 则撕裂⼩;
float textureX = var_textureCoords.x + (needOffset ? jitter : (jitter * amplitude * 0.006));
// 纹理撕裂后的x,y坐标
vec2 textureCoords = vec2(textureX, var_textureCoords.y);
// 纹素偏移
vec4 texel = texture2D(un_texture, textureCoords);
vec4 texelR = texture2D(un_texture, textureCoords + vec2(colorR_Offset * amplitude, 0.0));
vec4 texelB = texture2D(un_texture, textureCoords + vec2(colorB_Offset * amplitude, 0.0));
gl_FragColor = vec4(texelR.r, texel.g, texelR.b, texel.a);
}
眩晕滤镜
眩晕滤镜的实现可以通过残影和颜色偏移的叠加。
残影的效果: 是在移动的过程中,每经过⼀段时间间隔,根据当前的位置去创建⼀个新层,并且新层的不透明度随着时间逐
渐减弱。于是在⼀个移动周期内,可以看到很多透明度不同的层叠加在⼀起,从⽽形成残影的效果。残影,让图⽚随着时间
做圆周运动
颜⾊偏移: 物体移动的过程是蓝⾊在前⾯,红⾊在后⾯。所以整个过程可以理解成:在移动的过程中,每间隔⼀段时间,遗
失了⼀部分红⾊通道的值在原来的位置,并且这部分红⾊通道的值,随着时间偏移,会逐渐恢复
precision highp float;
uniform sampler2D un_texture;
varying vec2 var_textureCoords;
uniform float Time;
const float PI = 3.141592653589793;
const float duration = 2.0;
// 在每一个时间间隔后获得一个新的层
vec4 getTexelLevel(float time, vec2 textureCoords, float padding){
// 圆周坐标
vec2 translation = vec2(sin(time * (PI * 2.0 / duration)));
// 新的纹理坐标 = 旧纹理坐标 + 偏移量 * 圆周坐标,
vec2 translationTextureCoord = textureCoords + padding * translation;
// 返回一个新的图层坐标
return texture2D(un_texture, translationTextureCoord);
}
// 获取某个时刻图层的当前的透明度
float levelAlphaProgress(float curTime, float hideTime ,float startTime){
// 前一个时间
float beforeTime = duration + curTime - startTime;
// 对时间取模
float laterTime = mod(beforeTime, duration);
// 返回最小的时间,作为当前图层的透明度
return min(laterTime, beforeTime);
}
void main()
{
// 一个时间周期,0~2.0
float timeInterval = mod(Time,duration);
// 放大倍数
float zoomSize = 1.3;
// 偏移量
float padding = 0.5 * (1.0 - 1.0 / zoomSize);
// 放大后的纹理坐标
vec2 textureCoords = vec2(0.5,0.5) + (var_textureCoords - vec2(0.5,0.5)) / zoomSize;
// 隐藏时间
float hideTime = 0.9;
// 时间间隔
float timeGap = 0.2;
// 实现的效果是残留红色
float maxAlphaR = 0.5;
float maxAlphaG = 0.05;
float maxAlphaB = 0.06;
// 获得一个新的图层坐标
vec4 texelLevel = getTexelLevel(timeInterval,textureCoords,padding);
float alphaR = 1.0;
float alphaG = 1.0;
float alphaB = 1.0;
// 最终的图层颜色
vec4 lastLevel = vec4(0,0,0,0);
// 开启循环,不断的衰减透明度
for(float f = 0.0; f < duration; f += timeGap){
float tmpTime = f;
//获取到0-2.0秒内所获取的运动后的纹理坐标
vec4 tmpLevel = getTexelLevel(tmpTime,textureCoords,padding);
//某个时刻创建的层,在当前时刻的红绿蓝的透明度
float tmpAlphaR = maxAlphaR - maxAlphaR * levelAlphaProgress(timeInterval,hideTime,tmpTime)/hideTime;
float tmpAlphaG = maxAlphaG - maxAlphaG * levelAlphaProgress(timeInterval,hideTime,tmpTime)/hideTime;
float tmpAlphaB = maxAlphaB - maxAlphaB * levelAlphaProgress(timeInterval,hideTime,tmpTime)/hideTime;
//累积每⼀层每个通道乘以透明度颜⾊通道
lastLevel = lastLevel + vec4(tmpLevel.r * tmpAlphaR,tmpLevel.g * tmpAlphaG,tmpLevel.b * tmpAlphaB,1.0);
// 透明度不断衰减
alphaR = alphaR - tmpAlphaR;
alphaG = alphaG - tmpAlphaG;
alphaB = alphaB - tmpAlphaB;
}
// 计算最终颜色
lastLevel = lastLevel + vec4(texelLevel.r * alphaR,texelLevel.g * alphaG,texelLevel.b * alphaB,1.0);
gl_FragColor = lastLevel;
}