zl程序教程

您现在的位置是:首页 >  工具

当前栏目

OpenGL ES 常见滤镜处理

ES 处理 常见 OpenGL 滤镜
2023-09-27 14:27:15 时间

图片滤镜实现思路

前提条件:能够用GLSL显示普通图片

思路

初始化(上下文,顶点数组,顶点数据,顶点缓存区,CAEAGLayer,绑定渲染缓存区/帧缓存区,获取图片路径并将图片->纹理,设置视口,link默认着色器)

创建CADisplayLink刷新图片

图片缩放滤镜实现思路

缩放滤镜实际上基本的原理:可以通过修改顶点坐标和纹理坐标的对应关系来实现

缩放滤镜.vsh

//顶点坐标 

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; 

}

灵魂出窍滤镜实现思路

灵魂出窍滤镜:是两个层的叠加,并且上面的那层随着时间的推移,会逐渐放大且不透明度逐渐降低,这里也用到了放大的效果,我们用片段着色器实现。

灵魂出窍滤镜. fsh 

//「灵魂出窍」看上去是两个层的叠加,并且上⾯的那层随着时间的推移,会逐渐放⼤且不透明度逐渐降 

低。这⾥也⽤到了放⼤的效果,我们这次⽤⽚段着⾊器来实现 

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~1 

//透明度(0-0.4) 

float alpha = maxAlpha * (1.0 - progress); 

//缩放⽐例(1.0 - 1.8) 

float scale = 1.0 + (maxScale - 1.0) * progress; 

//放⼤纹理坐标 

//将顶点坐标对应的纹理坐标的 x 值到纹理中点的距离,缩⼩⼀定的⽐例。这次我们是改变了纹理坐 

标,⽽保持顶点坐标不变,同样达到了拉伸的效果 

float weakX = 0.5 + (TextureCoordsVarying.x - 0.5) / scale; 

float weakY = 0.5 + (TextureCoordsVarying.y - 0.5) / scale; 

//获取放⼤的纹理坐标 

vec2 weakTextureCoords = vec2(weakX, weakY); 

//通过上⾯的计算,我们得到了两个纹理颜⾊值 weakMask 和 mask, weakMask 是在 mask 的基 

础上做了放⼤处理 

4//读取到放⼤后的纹理坐标的纹素的颜⾊值 

vec4 weakMask = texture2D(Texture, weakTextureCoords); 

//读取原始的纹理坐标对应的纹素的颜⾊值 

vec4 mask = texture2D(Texture, TextureCoordsVarying); 

//在GLSL 实现颜⾊混合⽅程式.默认颜⾊混合⽅程式 = mask * (1.0 - alpha) + weakMask * alpha 

//参考OpenGL 第⼆节课中的颜⾊混合 

//计算最终的颜⾊赋值给⽚元着⾊器的内置变量: gl_FragColor 

gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha; 

}

抖动滤镜实现思路

抖动滤镜:颜色偏移+微弱的放大效果

抖动滤镜.fsh

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;

 

    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;

 

    vec4 maskR = texture2D(Texture, ScaleTextureCoords + offsetCoords);

    vec4 maskB = texture2D(Texture, ScaleTextureCoords - offsetCoords);

    vec4 mask = texture2D(Texture, ScaleTextureCoords);

 

    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);

}

闪白滤镜实现思路

闪白滤镜:添加白色图层,白色图层的透明度随着时间变化

闪⽩滤镜.fsh 

precision highp float; 

//纹理采样器 

uniform sampler2D Texture; 

//纹理坐标 

varying vec2 TextureCoordsVarying; 

//时间撮 

uniform float Time; 

//PI 常量 

const float PI = 3.1415926; 

void main (void) { 

//⼀次闪⽩滤镜的时⻓ 

float duration = 0.6; 

//表示将传⼊的时间转换到⼀个周期内,即 time 的范围是 0 ~ 0.6 

float time = mod(Time, duration); 

//⽩⾊颜⾊遮罩 

vec4 whiteMask = vec4(1.0, 1.0, 1.0, 1.0); 

//amplitude 表示振幅,引⼊ PI 的⽬的是为了使⽤ sin 函数,将 amplitude 的范围控制在 0.0 ~ 1.0 

之间,并随着时间变化 

float amplitude = abs(sin(time * (PI / duration))); 

//获取纹理坐标对应的纹素颜⾊值 

vec4 mask = texture2D(Texture, TextureCoordsVarying); 

//利⽤混合⽅程式: 将纹理颜⾊与⽩⾊遮罩融合. 

5//注意: ⽩⾊遮罩的透明度会随着时间变化做调整 

//我们已经知道了如何实现两个层的叠加.当前的透明度来计算最终的颜⾊值即可。 

gl_FragColor = mask * (1.0 - amplitude) + whiteMask * amplitude; 

}

毛刺滤镜实现思路

毛刺滤镜:撕裂+微弱的颜色偏移

具体的思路是,我们让每⼀⾏像素随机偏移 -1 ~ 1 的距离(这⾥的 -1 ~ 1 是对于纹理坐标来说的),但是如果整个画⾯都偏移⽐较⼤的值,那我们可能都看不出原来图像的样⼦。所以我们的逻辑是,设定⼀个阈值,⼩于这个阈值才进⾏偏移,超过这个阈值则乘上⼀个缩⼩系数。

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

⽑刺滤镜.fsh 

precision highp float; 

//纹理采样器 

uniform sampler2D Texture; 

//纹理坐标 

varying vec2 TextureCoordsVarying; 

//时间撮 

uniform float Time; 

//PI 常量 

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); 

6// -1~1 像素随机偏移范围(-1,1) 

float jitter = rand(TextureCoordsVarying.y) * 2.0 - 1.0; // -1~1 

//判断是否需要偏移,如果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); 

//获取3组颜⾊: 根据撕裂计算后的纹理坐标,获取纹素的颜⾊ 

vec4 maskR = texture2D(Texture, textureCoords + vec2(colorROffset * amplitude, 0.0)); 

//获取3组颜⾊: 根据撕裂技术后的纹理坐标,获取纹素颜⾊ 

vec4 maskB = texture2D(Texture, textureCoords + vec2(colorBOffset * amplitude, 0.0)); 

//颜⾊主要撕裂: 红⾊和蓝⾊部分.所以只调整红⾊ 

gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a); 

}

幻觉滤镜实现思路

幻觉滤镜:残影和颜色偏移的叠加

残影的效果是在移动的过程中,每经过⼀段时间间隔,根据当前的位置去创建⼀个新层,并且新层的不透明度随着时间逐渐减弱。于是在⼀个移动周期内,可以看到很多透明度不同的层叠加在⼀起,从⽽形成残影的效果。残影,让图⽚随着时间做圆周运动

颜⾊偏移物体移动的过程是蓝⾊在前⾯,红⾊在后⾯。所以整个过程可以理解成:在移动的过程中,每间隔⼀段时间,遗失了⼀部分红⾊通道的值在原来的位置,并且这部分红⾊通道的值,随着时间偏移,会逐渐恢复.

 

幻觉滤镜.fsh 

precision highp float; 

//纹理采样器 

uniform sampler2D Texture; 

//纹理坐标 

varying vec2 TextureCoordsVarying; 

//时间撮 

uniform float Time; 

//PI 常量 

const float PI = 3.1415926; 

//⼀次幻觉滤镜的时⻓ 

const float duration = 2.0; 

7//这个函数可以计算出,在某个时刻图⽚的具体位置。通过它我们可以每经过⼀段时间,去⽣成⼀个新的 

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) { 

//duration+currentTime-startTime % duration 

float time = mod(duration + currentTime - startTime, duration); 

return min(time, hideTime); 

}

void main (void) { 

//表示将传⼊的时间转换到⼀个周期内,即 time 的范围是 0 ~ 2.0 

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; 

//时间间隔 

8float timeGap = 0.2; 

//注意: 只保留了红⾊的透明的通道值,因为幻觉效果残留红⾊. 

//新图层的-R⾊透明度 0.5 

float maxAlphaR = 0.5; // max R 

//新图层的-G⾊透明度 0.05 

float maxAlphaG = 0.05; // max G 

//新图层的-B⾊透明度 0.05 

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; 

//获取到0-2.0秒内所获取的运动后的纹理坐标 

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); 

//透明度递减 

9alphaR -= tmpAlphaR; 

alphaG -= tmpAlphaG; 

alphaB -= tmpAlphaB; 

}

//最终颜⾊ += 红绿蓝 * 透明度 

resultMask += vec4(mask.r * alphaR, mask.g * alphaG, mask.b * alphaB, 1.0); 

//将最终颜⾊填充到像素点⾥. 

gl_FragColor = resultMask; 

}