张风捷特烈 Flutter & GLSL 系列文章:
- 《Flutter & GLSL#1 | Shader 让绘制无限强大》
- 《Flutter & GLSL#2 | 从坐标到颜色》
- 《Flutter & GLSL#3 | 变量传参》
- 《Flutter & GLSL#4 | 从条纹到马赛克》
- 《Flutter & GLSL#5 | 图形区域控制》
- 《Flutter & GLSL#6 | 平滑过渡 smoothstep》
- 《Flutter & GLSL#7 | 减法与线》
- 《Flutter & GLSL#8 | 乘法与矩形》
案例代码开源地址 【skeleton】
1. 回首 step 函数
step 是 GLSL 中内置的函数,其逻辑非常简单:比较两个数的大小,前者 < 后者时返回 0, 否则返回 1。
c
float step(float a, float b) {
return a < b ? 0 : 1;
}
先来看一下下面的着色器 step(0.1,coo.x); 返回的值,当 x < 0.1 时为 0 。使用返回值作为 rgb 三个颜色的通道,fragColor 也就是 (0,0,0,1) ,于是 x < 0.1 的区域显示出黑色。 同理 [0.1,1] 区域显示 (1,1,1,1) 白色:
c
---->[shaders/rect/r01_rect_step_1.frag]----
#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;
out vec4 fragColor;
uniform vec2 uSize;
void main() {
vec2 coo = FlutterFragCoord() / uSize;
float ret = step(0.1,coo.x);
vec3 color = vec3(ret);
fragColor = vec4(color,1.0);
}
2. 初探矩形形状
同理,如果将结果取 step(0.1,coo.y)
,在纵坐标小于 0.1 的区域内将会展示黑色:
现在问题来了,如果想要将横竖两个条纹同时存在,该怎么办呢? 仔细思考一下,图中的黑色等价于结果的数值 0
, 白色等价于结果的数值 1
。也可以将其视为 true/false :
0 和 1 的乘法具有 或
的性质, 即:
0 * 0 = 0
: 两个黑色结果叠加,输出 0 黑色。 (true|true) = true
0 * 1 = 0
: 黑色和白色结果叠加,输出 0 黑色。 (true|false) = true
1 * 1 = 1
: 白色和白色结果叠加,输出 1 白色。 (false|false) = false
所以,只要将两个结果相乘,就可以合并两个黑色内容。黑色内容之外的白色区域就可以视为一个 矩形:
c
---->[shaders/rect/r01_rect_step_2.frag]----
#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;
out vec4 fragColor;
uniform vec2 uSize;
void main() {
vec2 coo = FlutterFragCoord() / uSize;
float ret1 = step(0.1, coo.x);
float ret2 = step(0.1, coo.y);
vec3 color = vec3(ret1 * ret2);
fragColor = vec4(color, 1.0);
}
同理,叠加右侧和下方的黑色区域,就可以得到 左上角坐标 (0.1,0,1);边长是 0.8 的正方形,如下所示:
右侧黑色条纹,可以通过 step(0.1, 1 - coo.x)
得到,也就是 0.1 < 1 - coo.x
时, 即 coo.x < 0.9
,结果为 0 展示白色,下方同理。将四个数乘起来,就可以得到最终结果:
c
---->[shaders/rect/r01_rect_step_3.frag]----
#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;
out vec4 fragColor;
uniform vec2 uSize;
void main() {
vec2 coo = FlutterFragCoord() / uSize;
float left = step(0.1, coo.x);
float top = step(0.1, coo.y);
float right = step(0.1, 1 - coo.x);
float bottom = step(0.1, 1 - coo.y);
vec3 color = vec3(right * top * left * bottom);
fragColor = vec4(color, 1.0);
}
3. 矩形形状的封装
现在问题来了,如何呈现一个 指定坐标
、指定宽高
的矩形呢?比如下面由四个矩形构成的图案:
这里的核心是根据坐标和尺寸确定右下角坐标 br
,从图形关系上不难分析出
vec2 br = vec2(pos.x + size.x, pos.y + size.y);
定义横坐标来说, coo.x < br.x 展示白色,值为 0 。即 right = step(coo.x, br.x) ; 底部同理,所以可以封装为如下的方法:
scss
---->[shaders/rect/r01_rect_step_4.frag]----
#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;
out vec4 fragColor;
uniform vec2 uSize;
float rect(vec2 coo, vec2 pos, vec2 size) {
float left = step(pos.x, coo.x);
float top = step(pos.y, coo.y);
vec2 br = vec2(pos.x + size.x, pos.y + size.y);
float right = step(coo.x, br.x);
float bottom = step(coo.y, br.y);
return right * top * left * bottom;
}
void main() {
vec2 coo = FlutterFragCoord() / uSize;
float rect1 = rect(coo, vec2(0.2, 0.25), vec2(0.6, 0.1));
float rect2 = rect(coo, vec2(0.2, 0.45), vec2(0.25, 0.1));
float rect4 = rect(coo, vec2(0.55, 0.45), vec2(0.25, 0.1));
float rect3 = rect(coo, vec2(0.2, 0.65), vec2(0.6, 0.1));
vec3 color = vec3(rect1 + rect2 + rect3 + rect4);
fragColor = vec4(color, 1.0);
}
4. 高维向量的 step 函数
step 函数不仅仅作用于数字,也可以作用于高维的向量。其作用是对两个值在各个分量上做 step 处理:
c
float left = step(pos.x, coo.x);
float top = step(pos.y, coo.y);
vec2 lt = vec2(left,top);
等价于:
vec2 lt = step(pos, coo);
所以可以将 rect 方法的代码简化为:
c
float rect(vec2 coo, vec2 pos, vec2 size) {
vec2 br_pos = vec2(pos.x + size.x, pos.y + size.y);
vec2 lt = step(pos, coo);
vec2 br = step(coo, br_pos);
return lt.x * lt.y * br.x * br.y;
}
最后,将白色区域作用于贴图纹理,就可以在白色区域内展示图像。下面将黑色区域设置为透明度 0.2 ,白色区域显示原图像:
c
---->[shaders/rect/r01_rect_step_5.frag]----
#version 460 core
#include <flutter/runtime_effect.glsl>
precision mediump float;
out vec4 fragColor;
uniform vec2 uSize;
uniform sampler2D uTexture;
float rect(vec2 coo, vec2 pos, vec2 size) {
vec2 br_pos = vec2(pos.x + size.x, pos.y + size.y);
vec2 lt = step(pos, coo);
vec2 br = step(coo, br_pos);
return lt.x * lt.y * br.x * br.y;
}
void main() {
vec2 coo = FlutterFragCoord() / uSize;
float rect1 = rect(coo, vec2(0.2, 0.25), vec2(0.6, 0.1));
float rect2 = rect(coo, vec2(0.2, 0.45), vec2(0.25, 0.1));
float rect4 = rect(coo, vec2(0.55, 0.45), vec2(0.25, 0.1));
float rect3 = rect(coo, vec2(0.2, 0.65), vec2(0.6, 0.1));
float ret = rect1 + rect2 + rect3 + rect4;
vec4 color = texture(uTexture, coo);
fragColor = color * max(0.2, ret);
}
本文主要分析了 GLSL 中矩形形状的展现方式。了解通过乘法可以实现 0
和 1
组合的 或
关系。那本文就到这里,后期还会带来更多 GLSL 相关的文章,谢谢观看~