UnityShader

十六、利用GLSL实现动态滤镜

2020-08-16  本文已影响0人  夏天的枫_

工程是在另一文:[利用GLSL实现分屏滤镜] 基础上增加滤镜功能;核心是在着色器程序的编写。

先来喵一波


喵之滤镜

缩放滤镜
缩小放大是通过改变顶点坐标和纹理坐标的对应关系来实现缩放,纹理坐标不变,对顶点坐标进行一定程度的缩小放大。定义一个动画循环,让它的顶点坐标在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;
   
}
上一篇 下一篇

猜你喜欢

热点阅读