普通图片加载的顶点着色器和片元着色器代码如下:
顶点着色器:
//顶点坐标
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);
}
分析:图片有一个放大的过程,然后再回弹,那么就可以通过修改顶点坐标和纹理坐标的对应关系来实现。
修改顶点坐标和纹理坐标,既可以在顶点着色器实现,也可以在片元着色器上实现,此次效果在顶点着色器上实现。
顶点着色器:
//顶点坐标
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;
分析:由图中可以发现此效果有多个图层,最下面的图层不动,上面的图层随着时间的变化变大,并且透明度变低直至透明。
此效果是由多个图层构成,那么就需要颜色混合,所以此效果需要在片元着色器中实现,顶点着色器不变。
片元着色器:
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;
}
分析:图层变大,并且颜色发生了偏移,然后所以的再变回原来的效果。
此效果修改片元着色器:
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);
}
分析:在原图层上,添加一个白色图层,并根据时间戳,透明度发生变化。
片元着色器:
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;
}
分析:图片有一种撕裂的感觉,并且颜色发生偏移。
我们让每⼀⾏像素随机偏移 -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);
}
还在分析中,先附上代码,稍后补充上思路。
片元着色器:
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;
}