前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter & GLSL - 伍 | 图形区域控制

Flutter & GLSL - 伍 | 图形区域控制

作者头像
张风捷特烈
发布2024-02-22 10:19:30
1180
发布2024-02-22 10:19:30
举报
Flutter & GLSL 系列文章:

案例代码开源地址 【skeleton】

shader5.png
shader5.png

1、从圆形与 step 函数

有时我们需要通过着色器来表现图形,那如何通过坐标控制颜色值的输出,得到基本图形呢?之前一直强调:

shader 的奥义在于 通过坐标控制像素的颜色信息

想要展示一个半径为 r 的黑色圆形,只需要计算 像素点 距原点距离 len , 对于所有 len <= r 的像素点着为黑色;反之着为白色:

image.png
image.png

这个逻辑由下面的 circle 方法进行处理:当 len <= r 时返回 0 ,着色为 vec4(0, 0, 0, 1) 即黑色;反之返回 1 , 着色为 vec4(1, 1, 1, 1) 即白色。这样就通过圆的性质,通过对坐标点,控制像素的表现,形成图形。

内置函数 length(vec2) : 用于计算 vec2 坐标到原点的距离。

代码语言:javascript
复制
#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);
    if (len <= r) {
        return 0;
    } else {
        return 1;
    }
}

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

在 GLSL 中内置了一个用于生成阶梯的 step 函数:

内置函数 step(a,b) : 比较两个值 a, b ; 如果 a < b,则返回 0.0、否则返回 1.0。

也就是说,上面 circle 函数的比较逻辑可以简化成如下形式,效果是完全等价的:

代码语言:javascript
复制
float circle(vec2 coo, float r) {
    float len = length(coo);
    return step(r, len);
}

2、坐标系的转变

目前坐标系的原点在左上角(下图左),x,y 的取值范围在 [0,1]。所以上面画的圆形只显示了四分之一。如何变化,可以使坐标系的原点在画板中心(下图右),并且横纵坐标取值范围在 [-1,1] 呢?

image.png
image.png

其实很简单,左侧坐标系值放大两倍,即 坐标 *2 可以得到 x,y 的取值范围在 [0,2]的坐标系;然后坐标轴右移 1 个单位,即可得到 x,y 的取值范围在 [-1,1]的目标坐标系。

此时距离原点小于 0.5 的点被着为黑色,就可以得到如下的圆形:

image.png
image.png
代码语言:javascript
复制
---->[shaders/base_01_circle_step2.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 len = length(coo);
    return step(r, len);
}

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    /// 变换坐标系,以中心为原点
    coo = coo * 2 - 1;
    float ret = circle(coo, 0.5);
    fragColor = vec4(ret, ret, ret, 1);
}

3. 多个圆形联合

现在想一个小问题:如何将圆形呈白色,周围是黑色呢?很简单,用 1 - step(r, len) 即可,这样原来的黑色 1 就会变为白色 1-1 = 0 ; 原来的白色 0 就会变为白色 1-0 = 1

根据 step 的作用,不难推出: 1 - step(r, len) = step(len, r)

image.png
image.png
代码语言:javascript
复制
---->[shaders/base_01_circle_step3.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 len = length(coo);
    return step(len, r);
}

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

现在再想一想,如何在界面上显示多个圆呢?如下所示:

image.png
image.png

代码中有两个半径为 0.2 的小圆 c1 和 c2 ,可以自己思考一下 ret = c0 + c1 + c2 为什么可以把小圆展示出来?

  • 对每个像素操作 的视角来看,返回 1 表示该像素点是白色,返回 0 表示黑色;
  • c0 + c1 表示每个像素点的值是两个圆的结果累加值。把当前坐标像素的计算的结果相加: c0 是 0 (黑色); c1 是 1 (白色),两者相加 0+1 = 1 。就表示当前像素为白色。这样 c1 的白色就会出现在屏幕上。以此类推。
  • 当白色重叠时,即两个圆相交的地方,累加值是 1+1=2; 最后让 ret = min(ret, 1.0),就可以使结果中大于 1 时取 1 值:
代码语言:javascript
复制
---->[shaders/base_01_circle_step4.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 len = length(coo);
    return step(len, r);
}

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = 0;
    float c0 = circle(coo, 0.5);
    vec2 offset = vec2(-0.6, -0.6);
    float c1 = circle(coo + offset, 0.2);
    float c2 = circle(coo - offset*0.7, 0.2);
    ret = c0 + c1 + c2;
    ret = min(ret, 1.0);
    fragColor = vec4(ret, ret, ret, 1);
}

如下,我们将累加后的 ret 值减半,就更能说明问题。这样:

  • 未相交的圆 ret = (1+0)*0.5 = 0.5 ;颜色就是 vec4(0.5, 0.5, 0.5, 1) 为灰色。
  • 相交时 ret = (1+1) * 0.5 = 1 ;颜色就是 vec4(1, 1, 1, 1) 为白色。
image.png
image.png

于是,界面上可以呈现出叠合处更亮的效果:

代码语言:javascript
复制
---->[shaders/base_01_circle_step5.frag]----
void main() {
    /// 略同...
    ret = c0 + c1 + c2;
    ret *=0.5;
    ret = min(ret, 1.0);
    fragColor = vec4(ret, ret, ret, 1);
}

4. 区域控制与贴图

我们可以根据 circle 计算的结果是 0 还是 1 来控制纹理贴图对应坐标的像素颜色。这样就很容易实现对贴图 区域控制 的效果, 如下所示,当像素点位于白色圆区域时展示图片颜色。以此可以实现类似图片裁剪的效果:

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

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

float circle(vec2 coo, float r) {
    float len = length(coo);
    return step(len, r);
}

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = 0;
    float c0 = circle(coo, 0.5);
    vec2 offset = vec2(-0.6, -0.6);
    float c1 = circle(coo + offset, 0.2);
    float c2 = circle(coo - offset * .7, 0.2);
    ret = c0 + c1 + c2;
    ret = min(ret, 1.0);

    vec2 picCoo = (coo + 1) / 2;
    vec4 color = texture(uTexture, picCoo);
    /// 白色圆区域展示图片颜色
    color = ret == 1 ? color : vec4(0, 1, 1, 1);
    fragColor = color;
}

有了区域控制的手段,也可以很轻松地实现 区域贴图特效,如下所示,将原本黑色的区域施加马赛克的效果,就可以实现区域外的局部马赛克:

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

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

float circle(vec2 coo, float r) {
    float len = length(coo);
    return step(len, r);
}

void main() {
    vec2 coo = FlutterFragCoord() / uSize;
    coo = coo * 2 - 1;
    float ret = 0;
    float c0 = circle(coo, 0.5);
    vec2 offset = vec2(-0.6, -0.6);
    float c1 = circle(coo + offset, 0.2);
    float c2 = circle(coo - offset * .7, 0.2);
    ret = c0 + c1 + c2;
    ret = min(ret, 1.0);
    vec2 picCoo = (coo + 1) / 2;
    vec4 color = texture(uTexture, picCoo);

    if (ret == 0) {
        /// 局部马赛克
        float rowCount = 40.0;
        float x = floor(picCoo.x * rowCount) / rowCount;
        float y = floor(picCoo.y * rowCount) / rowCount;
        color = texture(uTexture, vec2(x, y));
    }
    fragColor = color;
}

本篇通过图形区域,让我们又多了一种对像素坐标控制颜色输出的手段。除了圆形之外,还有其他很多的基础图形区域,将在后面继续介绍。那本文就到这里,谢谢观看~

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Flutter & GLSL 系列文章:
  • 1、从圆形与 step 函数
  • 2、坐标系的转变
  • 3. 多个圆形联合
  • 4. 区域控制与贴图
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档