美文网首页
OpenGL案例 - 动态滤镜(1)

OpenGL案例 - 动态滤镜(1)

作者: 卡布奇诺_95d2 | 来源:发表于2020-09-18 18:04 被阅读0次

由于着色器文件中加入中文注释后会产生不明问题,为了以后复习时能更好的理解如何实现动态滤镜效果,本文将逐步描述着色器文件中实现动态滤镜效果,其它的openGL绘制图像等操作可查看文末的Demo源码。

缩放滤镜

动态缩放效果图

动态缩放.gif

实现原理:

  • 将当前的顶点坐标乘以缩放因子能实现放大或缩小的效果。
  • 缩放因子随着正弦曲线进行变化时,即可实现动态效果。

实现步骤

1. 实现缩放效果

这个步骤比较简单,拿到顶点坐标之后,将其乘以一个缩放因子就可以。注意点:缩放因子最好是float类型,否则可能缩放不成功

放大效果:将顶点坐标乘以一个大于1.0的数。
缩小效果:将顶点坐标乘以一个大于0.0,小于1.0的数
放大效果示例:

gl_Position = vec4(Position.x * 1.5, Position.y * 1.5, Position.zw);
放大.gif

缩小效果示例:

gl_Position = vec4(Position.x * 0.5, Position.y * 0.5, Position.zw);
缩小.gif

2. 实现动态效果

正弦曲线是一个从0 -> 1 -> 0 -> -1 ->0循环变化的曲线。这符合我们的需求,即让缩放因子0 -> 1 -> 0循环变化,即可形成一个动态的效果。由于由于缩放因子不能为负数,所以我们只需要正弦曲线的[0,π]。下图为[0,π]的正弦曲线。

正弦曲线
如何形成动态效果的方法有了。现在只剩下如何产生一个正弦变化的缩放因子
一提到变化,整个项目中随时都在变化的就是时间了。我们可以将时间通过取模运算,让其它在指定区间内变化 。
有了以上的理论基础,可以修改着色器文件了。以下着色器代码以动态放大效果为目的。
//定义一次动态变化的时长,即完成一次 0->1->0 所需要的时间
float duration = 0.6;

//其中Time为当前时间,mod为取模运算,经过mod运算后,time的取值范围为[0, duration];
float time = mod(Time, duration);

//定义一个最大放大倍数
float maxAmplitude = 0.3;

//amplitude:表示振幅,通过正弦函数计算的振幅范围为[0,0.3],加上原本图像缩放比例为1(即不缩放),即振幅的变化范围为[1,1.3]。
float amplitude = 1.0 + maxAmplitude * abs(sin(time * PI/duration));

//最后将顶点坐标乘以当前计算的振幅,就可达到缩放效果
gl_Position = vec4(Position.x * amplitude, Position.y * amplitude, Position.zw);

灵魂出窍滤镜

效果图

灵魂出窍效果.gif

实现原理

从效果图中看到灵魂出窍的效果其实就是两个图层的叠加,将一个放大且透明度在慢慢变小的图层叠加到原始图层中。

  • 将纹理坐标均匀放大。
  • 将放大后的纹素与原始纹素进行混合。

实现步骤

由于需要将两个纹素进行颜色混合,所以必须修改片元着色器,之前所操作的放大效果是在顶点着色器完成,这里引入一个方法实现新的放大效果,即在片元着色器中修改纹理坐标

实现纹理均匀放大

获取一个均匀变化的常量在之前的缩放滤镜效果中已经描述过,这里采用相似的方法。不同的是,透明度的变化是一个[1,0]的过程,所以需要根据时间变化设定一个[0,1]的范围

//动画持续时长
float duration = 0.7;

//将当前时间限制在[0,duration]变化范围
float time = mod(Time, duration);

//定义一个百分比,即time/duration,此时就可以将这个百分比限定在[0,1]范围
float process = time/duration;

由于最终效果是两个图层混合,而位于上层(即放大效果的图层)的透明度如果为1的话,则下层将会被完全遮挡,因此,上层的透明度必须小于1.0。

//设定上层透明度的最大值
float maxAlpha = 0.4;
//根据百分比,计算一个[0,maxAlpha]的透明度变量
float alpha = maxAlpha * (1.0 - process);

根据百分比,计算均匀变化的放大因子变量

//设定上层放大因子的最大值
float maxScale = 0.4;
//根据百分比,计算一个[0, maxScale]的放大因子变量
float scale = 1.0 + maxScale * process;

在实现纹理放大之前,先来了解一下纹理放大的原理。
下图为纹理未放大前,为了便于观察,绿色透明区域为顶点坐标对应的范围,顶点坐标与纹理坐标呈一一对应的关系,即:

  • 顶点的(0,0)与纹理的(0,0)对应
  • 顶点的(0,1)与纹理的(0,1)对应
  • 顶点的(1,0)与纹理的(1,0)对应
  • 顶点的(1,1)与纹理的(1,1)对应
正常情况.png

假设将当前纹理放大1.3倍。则顶点坐标与纹理坐标对应关系如下:

  • 顶点的(0,0)与纹理的(0,0)对应
  • 顶点的(0,1)与纹理的(0,0.77)对应
  • 顶点的(1,0)与纹理的(0.77,0)对应
  • 顶点的(1,1)与纹理的(0.77,0.77)对应

注意:0.77 = 1.0/1.3

放大1.3倍.png

根据这个原理,来尝试一下纹理放大后。此处暂时不加入混合,只实现一下放大效果。

//放大纹理的X坐标
float weakX = (TextureCoordsVarying.x)/scale;
//放大纹理的Y坐标
float weakY = (TextureCoordsVarying.y)/scale;
//得到放大后的纹理坐标
vec2 weakTextureCoords = vec2(weakX, weakY);
//将放大后的纹理坐标赋值给内建变量
gl_FragColor = weakMask;

纹理放大效果图如下:


纹理放大效果1.gif

从效果图上看,确实实现了放大效果,但这与我们最终想要的存在差异。由于中心点的位置是不变的,那可以根据中心点操作完成纹理放大。

  • 实现中心点的左边放大
float weakX, weakY;
    if(TextureCoordsVarying.x <= 0.5){
        weakX = (0.5 - TextureCoordsVarying.x)/scale;
    }
    else{
//        weakX = (TextureCoordsVarying.x - 0.5)/scale;

    }
中心点左边放大.gif
  • 实现中心点的右边放大
if(TextureCoordsVarying.x <= 0.5){
//        weakX = (0.5 - TextureCoordsVarying.x)/scale;
    }
    else{
        weakX = (TextureCoordsVarying.x - 0.5)/scale;
    }
中心点右边放大.gif
  • 合并左右两边放大的效果
float weakX, weakY;
    if(TextureCoordsVarying.x <= 0.5){
        weakX = 0.5 - (0.5 - TextureCoordsVarying.x)/scale;
    }
    else{
        weakX = (TextureCoordsVarying.x - 0.5)/scale + 0.5;
    }
    
//上面整个if判断的代码等价于下面代码
float weakX = 0.5 + (TextureCoordsVarying.x - 0.5)/scale;

//上面的代码与下面的代码是等价的
vec2 weakTextureCoords = vec2(0.5, 0.5) + (TextureCoordsVarying - vec2(0.5, 0.5))/scale;
左右放大效果合并.gif

同理,Y方向放大也是一样的实现方法。

float weakY = 0.5 + (TextureCoordsVarying.y - 0.5)/scale;
修改纹理坐标实现放大效果.gif

实现纹理颜色混合

最终的效果图中,上面图层是一个逐渐放大且透明度变低的过程。下面图层是一个原始图片。此时需要将上面图层与下面图层进行颜色混合。
混合的方式有两种:

  • mix函数方法
gl_FragColor = mix(mask, weakMask, alpha);
  • 调用混合方程式
//这里使用默认方程式
vec4 weakMask = texture2D(Texture, weakTextureCoords);
vec4 mask = texture2D(Texture, TextureCoordsVarying);
gl_FragColor = weakMask * alpha + mask * (1.0 - alpha);

Demo源码详见:Demo地址中的缩放滤镜和灵魂出窍滤镜着色器文件。

相关文章

网友评论

      本文标题:OpenGL案例 - 动态滤镜(1)

      本文链接:https://www.haomeiwen.com/subject/mpfryktx.html