前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter & GLSL - 柒 | 减法与线

Flutter & GLSL - 柒 | 减法与线

作者头像
张风捷特烈
发布2024-02-26 08:25:09
1450
发布2024-02-26 08:25:09
举报
文章被收录于专栏:Android知识点总结
Flutter & GLSL 系列文章:

案例代码开源地址 【skeleton】

shader7.png
shader7.png

前面我们通过圆形的区域和平滑过渡,认识了两个非常重要的内置函数 stepsmoothstep。其中这两个方法本质上是非常简单的,GLSL 中内置它们是因为非常通用,GPU 对其有特殊的优化,从而可以被硬件加速。

代码语言:javascript
复制
float step(float a, float b) {
    return a < b ? 0 : 1;
}

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

1. 图形的减法

现在思考一下,如果想要实现圆形边线 的图形,该怎么办呢?思路其实很简单,如下左图是一个 r=0.6 的圆;右图将该圆减去 r=0.5 的圆,就可以得到圆环;当圆环的宽度变小,就可以得到 圆形线

现在问题关键在于如何对两个图形进行 减法操作。 上一篇中将实心圆形封装为如下的 circle 方法:

  • coo 表示坐标;
  • r 表示圆的半径,
  • t 表示过渡的阈值宽度:
代码语言:javascript
复制
float circle(vec2 coo, float r, float t) {
    float len = length(coo);
    return 1 - smoothstep(r, r + t, len);
}

函数返回值是颜色通道的浮点型数字。黑色返回 0 、白色返回 1 、过渡区域在 0~1间渐变。仔细想一想,如果两个形状像素点重合,如果都是白色 1-1 = 0 就变成了黑色;如果都是黑色 0-0 = 0 保持黑色。

所以 circle 函数返回值的加减法在视觉上可以增加和减去图形。

代码语言:javascript
复制
---->[shaders/base_03_line_step1.frag]----
#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;

out vec4 fragColor;
uniform vec2 uSize;

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

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = circle(coo, 0.6, 0.01);
    // 减去 r = 0.5 的圆
    ret -= circle(coo, 0.5, 0.01);
    fragColor = vec4(ret, ret, ret, 1);
}

2. 圆形线的封装

上面我们通过两个圆相减实现了圆形线,现在来推演一下如何封装一个 圆形线方法 circle_line。 如下所示增加 w 参数表示线的宽度:

  • 演绎第一阶段:将圆的相减逻辑封装在 circle_line 内部
代码语言:javascript
复制
// coo  :  像素坐标
// r    :  圆半径
// w    :  边线宽度
// t    :  过渡阈值
float circle_line(vec2 coo, float r, float w, float t) {
    float len = length(coo);
    float c1 = 1 - smoothstep(r + w, r + w + t, len);
    float c2 = 1 - smoothstep(r, r + t, len);
    return c1 - c2;
}

对于边线来说,当线宽较大不可忽略时,想要考虑线宽在图形外、中、内,上面的方法边线在圆形之外。下面用一个

r=0.6 , 边线 w = 0.4 的圆说明一下

  • 演绎第二阶段:添加边线溢出控制 boder_out ,为 0 时表示不溢出,也就是边线在圆内;1 全部溢出,边线在圆外;0.4 表示 40% 的变现露出,60%的边线在园内。
代码语言:javascript
复制
// coo        :  像素坐标
// r          :  圆半径
// w          :  边线宽度
// t          :  过渡阈值
// boder_out  :  边线溢出控制 0:内侧 1:外侧 0.5: 增加
float circle_line(vec2 coo, float r, float w, float t, float boder_out) {
    float len = length(coo);
    float outW = w * boder_out;
    float inW = w - outW;
    float c1 = 1 - smoothstep(r + outW, r + outW + t, len);
    float c2 = 1 - smoothstep(r - inW, r -inW + t, len);
    return c1 - c2;
}

3. 再认知 smoothstep

上一节我们介绍过 smoothstep 的作用,以及三个参数的含义:

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

image.png
image.png

现在思考一个问题, smoothstep(e0,e1,v)smoothstep(0,e1-e0,v-e0) 两者是否等价?根据定义不难分析出, 后者表示:

v-e0 < 0 时, 返回 0; v-e0 > e1-e0 时, 返回 1; v-e0 在 [0,e1-e0] 之间 时,通过曲线函数在 0~1 间过渡插值

可以看出两者只是参考物的不同, v-e0 < 0v < e0 在逻辑上是一致的。所以 smoothstep 中的三个参数同时加减数字,返回的结果保持不变。所以圆形函数也可以是如下逻辑:

代码语言:javascript
复制
float circle(vec2 coo, float r, float t) {
    float len = length(coo);
    return 1 - smoothstep(0,  t, len-r);
}

float circle_line(vec2 coo, float r, float w, float t, float boder_out) {
    float len = length(coo);
    float outW = w * boder_out;
    float inW = w - outW;
    return smoothstep(-inW, -inW + t, len - r)
    - smoothstep(outW, outW + t, len - r);
}

4. 循环遍历

glsl 中,可以使用 for 来执行循环逻辑,比如下面遍历生成很多条线圆形线,在循环体中可以根据次数 i 控制圆的半径、线宽、过渡阈值参数:

效果1

效果2

效果3

代码语言:javascript
复制
void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = 0;
    for (int i = 0;i < 40; i++) {
        float radius = 0.05 * i;
        //ret += circle_line(coo, radius, 0.01, 0.01, 0.5);// 效果1 
        ret += circle_line(coo, radius, 0.01 * (i / 8), 0.01, 0.5);// 效果2 
        //ret += circle_line(coo, radius, 0.01*(i/8), 0.01*i, 0.5);// 效果3 
    }
    fragColor = vec4(ret, ret, ret, 1);
}

颜色和纹理图片相结合,白色是图片展示区域,黑色不显示图片,过渡区域展示对应的透明色。所以将上面的圆线条纹施加到纹理上既可以得到如下效果:

image.png
image.png

代码中分为 40 条圆线,半径从内到外依次增加 0.025,将所有的圆线通过 + 号进行合并;最后将结果施加到图片纹理中:

代码语言:javascript
复制
#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;

out vec4 fragColor;
uniform sampler2D uTexture;
uniform vec2 uSize;

float circle_line(vec2 coo, float r, float w, float t, float boder_out) {
    float len = length(coo);
    float outW = w * boder_out;
    float inW = w - outW;
    return smoothstep(-inW, -inW + t, len - r)- smoothstep(outW, outW + t, len - r);
}

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = 0;
    for(int i=0;i<40;i++){
        float radius = 0.025*i;
        float t = 0.01;
        float w = 0.05;
        ret += circle_line(coo, radius, w, t, 0.5);
    }
    fragColor = vec4(ret, ret, ret, 1);
    vec2 picCoo = (coo + 1) / 2;
    vec4 color = texture(uTexture, picCoo);
    fragColor = color*ret;
}

这里如果改变宽度,比如 0.03 ,那么临近的两个圆值将会叠加,此时纹理对应的颜色将被 "增强"。w= 0.05 时,输出值会大于 1 ,可以看到图片被明显提亮。

本文通过 减法 认识了如何将两个形状进行裁剪,从而得到圆环和圆线。那本篇就到这里,后续还会带来更多 Flutter & GLSL 探索的文章,敬请期待 ~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Flutter & GLSL 系列文章:
  • 1. 图形的减法
  • 2. 圆形线的封装
  • 3. 再认知 smoothstep
  • 4. 循环遍历
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档