likes
comments
collection
share

Flutter & GLSL - 陆 | 平滑过渡 smoothstep

作者站长头像
站长
· 阅读数 1

Flutter & GLSL 系列文章:

案例代码开源地址 【skeleton】

Flutter & GLSL - 陆 | 平滑过渡 smoothstep


1. 去除锯齿

在上一篇中,我们通过 step 函数通过 像素与原点的距离 控制输出的颜色,从而达到如下右图展示白色圆形区域。但仔细观察不难发现圆的四周非常锯齿非常明显,所以视觉上很不美观。本篇将介绍一下 smoothstep 函数让值可以平滑过渡。

有锯齿平滑过渡
Flutter & GLSL - 陆 | 平滑过渡 smoothstepFlutter & GLSL - 陆 | 平滑过渡 smoothstep

内置函数 smoothstep(e0,e1,v) : v < e0 时, 返回 0; v > e1 时, 返回 1; v 在 [e0,e1] 之间 时,通过曲线函数在 0~1 间过渡插值

这样的话,通过圆形区域控制纹理采样,就可以得到边缘光滑的图片,如下右图:

有锯齿平滑过渡
Flutter & GLSL - 陆 | 平滑过渡 smoothstepFlutter & GLSL - 陆 | 平滑过渡 smoothstep

smoothstep 方法可以让结果在 [e0,e1] 区间内逐渐变化,而不是像 step 非 0 即 1 的突然转变。下面的 circle 函数中,len 表示像素坐标距离,区间是 [r, r + 0.1]

  • 当 len 小于 r : smoothstep(r, r + 0.1, len) = 0 ;1-0 = 1,表示白色(如下内虚线区域)。
  • 当 len 大于 r : smoothstep(r, r + 0.1, len) = 1 ;1-1 = 0,表示黑色(如下外虚线外部)。
  • 当 在 [e0,e1] 之间 : smoothstep(r, r + 0.1, len) 会从 0~1 过渡插值,1 - 结果 就是从 1~0 的过渡渐变,也就是两个虚线间 由白到黑 的渐变过渡。

Flutter & GLSL - 陆 | 平滑过渡 smoothstep

#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;

out vec4 fragColor;
uniform vec2 uSize;

float circle(vec2 coo, float r) {
    float len = length(coo);
    return 1 - smoothstep(r, r + 0.1, len);
}

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = circle(coo, 0.5);
    fragColor = vec4(ret, ret, ret, 1);
}

2. 通过交互来控制过渡区域大小

前面介绍过 Flutter 向着色器中传参,如下所示,定义 uThreshold 变量控制渐变区域的大小。通过 Slider 的拖拽事件改变 uThreshold 的值,从而更直观地展示 smoothstep 的作用:

Flutter & GLSL - 陆 | 平滑过渡 smoothstep

#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;
uniform float uThreshold;
uniform vec2 uSize;
out vec4 fragColor;


float circle(vec2 coo, float r) {
    float len = length(coo);
    return 1 - smoothstep(r, r + uThreshold, len);
}

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = circle(coo, 0.5);
    fragColor = vec4(ret, ret, ret, 1);
}

3. 图片纹理和平滑过渡的结合

上节介绍过通过圆形区域来采样图片的颜色,这里也是类似。通过 color*ret 就可以达到想要的效果。因为:

  • 黑色时 ret = 0 ,任何颜色乘 0 后透明度都会变成 0 ,效果是不显示。
  • 白色时 ret = 1 ,任何颜色乘 1 不变,效果是原样显示。
  • 在中间的过渡区域内,即颜色的各个分量减少一定的百分比
uThreshold = 0.2uThreshold = 0.3
Flutter & GLSL - 陆 | 平滑过渡 smoothstepFlutter & GLSL - 陆 | 平滑过渡 smoothstep

这样就完成了图片边缘模糊渐变的小特效:

#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;
uniform vec2 uSize;
uniform float uThreshold;
uniform sampler2D uTexture;
out vec4 fragColor;


float circle(vec2 coo, float r) {
    float len = length(coo);
    return 1 - smoothstep(r, r + uThreshold, len);
}

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = circle(coo, 0.5);
    vec2 picCoo = (coo + 1) / 2;
    vec4 color = texture(uTexture, picCoo);

    fragColor = color*ret;
}

本文主要介绍 smoothstep 的使用,让某段区域可以平滑过渡,从而在视觉上消除锯齿或者其他过渡不和谐的转变。虽然 smoothstep 很强大,但是本质上是非常简单的。在 e0 ~ e1 直接通过 x * x * (3 - 2 * x) 函数进行平滑过渡:

float smoothstep(float e0, float e1, float x) {
    x = clamp((x - e0) / (e1 - e0), 0.0, 1.0); 
    return x * x * (3 - 2 * x);
}

也就是说过渡区域内输入的值,将被以如下的曲线转化输出:比如上面第一个案例区域在 [0.5,0.6],过渡时符合蓝框区域的曲线:

Flutter & GLSL - 陆 | 平滑过渡 smoothstep

那本文就到这里,后面还会带来更多 Flutter & GLSL 的知识,我们下次再见 ~