
前情提要
这篇滤镜效果的实现是在上一篇 分屏滤镜 的基础上来进行实现的,同样的前提是可以利用GLSL加载一张正常的图片。
缩放滤镜
缩放滤镜实际上基本的原理是可以通过修改顶点坐标和纹理坐标的对应关系来实现放大缩小效果。
这个放大缩小的实现其实可以在顶点着色器中实现,也可以在片元着色器中实现。(注意:在运行时,着色器代码中最好不要有中文)
顶点着色器代码:
// 顶点坐标attribute vec4 Position;// 纹理坐标attribute vec2 TextureCoords;varying vec2 TextureCoordsVarying;// 时间(通过uniform传入一个时间Time)uniform float Time;const float PI = 3.1415926;void main (void) {// 一次缩放效果时长float duration = 0.4;// 最大缩放幅度float maxAmplitude = 0.3;// 表示时间周期float time = mod(Time, duration);// 缩放幅度 [1.0,1.3]float amplitude = 1.0 + maxAmplitude * abs(sin(time * (PI / duration)));// 顶点坐标x/y 分别乘以放大系数[1.0,1.3]gl_Position = vec4(Position.x * amplitude, Position.y * amplitude, Position.zw);// 纹理坐标TextureCoordsVarying = TextureCoords;}
实现效果:
出窍滤镜
灵魂出窍滤镜的原理: 是两个层的叠加,并且上面的那层随着时间的推移,会逐渐放大且不透明度逐渐降低。这里也⽤到了放大的效果(基于缩放的原理),我们这次用片元着⾊器来实现该效果。
片元着色器代码:
precision highp float;// 纹理采样器uniform sampler2D Texture;// 纹理坐标varying vec2 TextureCoordsVarying;// 时间(通过uniform传入一个时间Time)uniform float Time;void main (void) {// 一次灵魂出窍效果的时长 1.0float duration = 1.0;// 透明度上限float maxAlpha = 0.5;// 放大图片上限float maxScale = 1.8;// 进度值[0,1]float progress = mod(Time, duration) / duration; // 0~1// 透明度范围[0,0.5]float alpha = maxAlpha * (1.0 - progress);// 缩放比例[1.0,1.8]float scale = 1.0 + (maxScale - 1.0) * progress;// 放大纹理坐标// 根据放大比例,得到放大纹理坐标 [0,0],[0,1],[1,1],[1,0]float weakX = 0.5 + (TextureCoordsVarying.x - 0.5) / scale;float weakY = 0.5 + (TextureCoordsVarying.y - 0.5) / scale;// 放大纹理坐标vec2 weakTextureCoords = vec2(weakX, weakY);// 获取对应放大纹理坐标下的纹素(颜色值rgba)vec4 weakMask = texture2D(Texture, weakTextureCoords);// 原始的纹理坐标下的纹素(颜色值rgba)vec4 mask = texture2D(Texture, TextureCoordsVarying);// 颜色混合 默认颜色混合方程式 = mask * (1.0-alpha) + weakMask * alpha;gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;}
实现效果:
可以看到这这个效果中,下面的那一层根本就没有变化,只是上面的一层做了放大,以此来造成视觉上的偏差。
抖动滤镜
抖动的过程中也是基于缩放的原理,而且它的颜色值产生一定的偏差。
抖动效果: 颜⾊偏移 + 微弱的放大效果
片元着色代码:
precision highp float;// 纹理uniform sampler2D Texture;// 纹理坐标varying vec2 TextureCoordsVarying;// 时间(通过uniform传入一个时间Time)uniform float Time;void main (void) {// 一次抖动滤镜的时长float duration = 1.0;// 放大图片上限float maxScale = 1.2;// 颜色偏移步长float offset = 0.02;// 进度[0,1]float progress = mod(Time, duration) / duration;// 颜色偏移值范围[0,0.02]vec2 offsetCoords = vec2(offset, offset) * progress;// 缩放范围[1.0-1.2];float scale = 1.0 + (maxScale - 1.0) * progress;// 放大纹理坐标.vec2 ScaleTextureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5)) / scale;// 获取3组颜色rgb// 原始颜色+offsetCoordsvec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);// 原始颜色-offsetCoordsvec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords);// 原始颜色vec4 mask = texture2D(Texture, ScaleTextureCoords);// 从3组来获取颜色:// maskR.r,mask.g,maskB.b 注意这3种颜色取值可以打乱或者随意发挥.只是效果会有不一样.// mask.a 获取原图的透明度gl_FragColor = vec4(mask.r, maskR.g, maskB.b, mask.a);}
实现效果:
闪白滤镜
闪白滤镜的原理: 在上层添加⽩色图层 ,⽩色图层的透明度随着时间的变化而变化。
片元着色器代码:
precision highp float;// 纹理采样器uniform sampler2D Texture;// 纹理坐标varying vec2 TextureCoordsVarying;// 时间(通过uniform传入一个时间Time)uniform float Time;void main (void) {// 一次闪白滤镜的时长float duration = 0.5;// 表示时间周期[0.0,0.5]float time = mod(Time, duration);// 白色颜色遮罩层vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0);// 振幅: (0.0,1.0)float amplitude = abs(sin(time * (PI / duration)));// 纹理坐标对应的纹素(RGBA)vec4 mask = texture2D(Texture, TextureCoordsVarying);// 利用混合方程式; 白色图层 + 原始纹理图片颜色 来进行混合gl_FragColor = mask * (1.0 - amplitude) + whiteMask * amplitude;}
实现效果:
毛刺滤镜
⽑刺滤镜的原理: 撕裂 + 微弱的颜⾊偏移。
片元着色器代码:
precision highp float;// 纹理uniform sampler2D Texture;// 纹理坐标varying vec2 TextureCoordsVarying;// 时间(通过uniform传入一个时间Time)uniform float Time;// 随机数float rand(float n) {//fract(x),返回x的小数部分数据return fract(sin(n) * 43758.5453123);}void main (void) {// 最大抖动float maxJitter = 0.06;// 一次毛刺滤镜的时长float duration = 0.5;// 红色颜色偏移量float colorROffset = 0.01;//绿色颜色偏移量float colorGOffset = -0.02;// 蓝色颜色偏移量float colorBOffset = -0.035;// 时间周期[0.0,1.0];float time = mod(Time, duration * 2.0);// 振幅:[0,1];float amplitude = max(sin(time * (PI / duration)), 0.0);// 像素随机偏移[-1,1]float jitter = rand(TextureCoordsVarying.y) * 2.0 - 1.0; // -1~1// 是否要做偏移.bool needOffset = abs(jitter) < maxJitter * amplitude;// 获取纹理X值.根据needOffset,来计算它X撕裂.// needOffset = YES,撕裂较大;// needOffset = NO,撕裂较小.float textureX = TextureCoordsVarying.x + (needOffset ? jitter : (jitter * amplitude * 0.006));// 撕裂后的纹理坐标x,yvec2 textureCoords = vec2(textureX, TextureCoordsVarying.y);// 颜色偏移3组颜色// 根据撕裂后获取的纹理颜色值vec4 mask = texture2D(Texture, textureCoords);// 撕裂后的纹理颜色偏移vec4 maskR = texture2D(Texture, textureCoords + vec2(colorROffset * amplitude, 0.0));vec4 maskG = texture2D(Texture, textureCoords + vec2(colorGOffset * amplitude, 0.0));vec4 maskB = texture2D(Texture, textureCoords + vec2(colorBOffset * amplitude, 0.0));// 颜色部分发生撕裂.gl_FragColor = vec4(maskR.r, maskG.g, maskB.b, mask.a);}
实现效果:
毛刺效果具体的思路是:我们让每一行像素随机偏移 -1 ~ 1 的距离(这里的 -1 ~ 1 是对于纹理坐标来说的),但是如果整个 画面都偏移⽐较大的值,那我们可能都看不出原来图像的样子。所以,设定⼀个阈值,⼩于这个阈值才进行偏移,超过这个阈值则乘上⼀个缩小系数。
则最终呈现的效果是:绝⼤部分的行都会进行微小的偏移,只有少量的行会进行较⼤偏移。
幻觉滤镜
⽑刺滤镜的原理:残影和颜⾊色偏移的叠加
片元着色器代码:
precision highp float;// 纹理采样器uniform sampler2D Texture;// 纹理坐标varying vec2 TextureCoordsVarying;// 时间戳uniform float Time;// PI常量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) {// 表示将传入的时间转换到一个周期内,即time的范围是 0~2.0float 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;// 注意:只保留了红色的透明的通道值,因为幻觉效果残留红色// 新图层的-R色透明度 0.5float maxAlphaR = 0.5; // max R// 新图层的-G色透明度 0.05float maxAlphaG = 0.05; // max G// 新图层的-B色透明度 0.05float maxAlphaB = 0.05; // max B// 最终图层颜色vec4 mask = getMask(time, textureCoords, padding);float alphaR = 1.0; // Rfloat alphaG = 1.0; // Gfloat alphaB = 1.0; // Bvec4 resultMask = vec4(0, 0, 0, 0);for (float f = 0.0; f < duration; f += timeGap) {// 获取到0~2.0秒内所获取的运动后的纹理坐标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;}
实现效果:
残影的效果: 是在移动的过程中,每经过⼀一段时间间隔,根据当前的位置去创建⼀一个新层,并且新层的不不透明度随着时间逐 渐减弱。于是在⼀一个移动周期内,可以看到很多透明度不不同的层叠加在⼀一起,从⽽而形成残影的效果。残影,让图⽚片随着时间 做圆周运动
颜⾊色偏移: 物体移动的过程是蓝⾊色在前⾯面,红⾊色在后⾯面。所以整个过程可以理理解成:在移动的过程中,每间隔⼀一段时间,遗 失了了⼀一部分红⾊色通道的值在原来的位置,并且这部分红⾊色通道的值,随着时间偏移,会逐渐恢复.
