Flutter & GLSL 系列文章:
1. 从尺度入参开端说起
上一篇介绍了,在着色器中坐标和色彩的联系,将坐标归 1 后留下一个问题:
怎么让着色器代码中的 size 不写死,由外界传递呢?
在着色器代码中,可以经过 uniform
界说 vec2 类型变量 uSize:
---->[shaders/var_01.frag]----
#version 460 core
precision mediump float;
#include <flutter/runtime_effect.glsl>
uniform vec2 uSize;
out vec4 fragColor;
void main() {
vec2 coo = FlutterFragCoord().xy/uSize;
fragColor = vec4(1,0.0,0.0,1.0);
}
Flutter 中经过 FragmentShader#setFloat
传递变量,如下所示:索引 0 表明 uSize 第一个重量,也便是宽度;索引 1 设置高度:
---->[lib/paint/shaders/var_demos/v1_painter.dart]----
class ShaderPainter extends CustomPainter {
ShaderPainter({required this.shader});
FragmentShader shader;
@override
void paint(Canvas canvas, Size size) {
shader.setFloat(0, size.width);
shader.setFloat(1, size.height);
final paint = Paint()..shader = shader;
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint,);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
这便是最简单从 Flutter 像 GLSL 传递参数的方式。
2. 挑选色彩进行突变
下面再经过一个事例了解一下入参的处理,咱们在下方预备了一些备选色,现在的需求是
将挑选的色彩作为入参,经过着色器展现
黑色 → 选中色
的突变作用:
道理是相同的,色彩是一个四维向量 vec4,作为参数传入着色器程序。界说 uniform vec4 uColor
; 然后经过 mix 函数将黑色和传入色彩,依据像素的横坐标进行混合。
mix 是一个内置函数,由三个入参 a,b,t 。表明用于在 a, b 个值在 t 分度时的线性混合。
举个小例子:8 和 24 在 0.4 处的混合值是8 + (24 -8)*0.4
对于多维的值,便是各个重量的混合值。
---->[shaders/var_02.frag]----
#version 460 core
precision mediump float;
#include <flutter/runtime_effect.glsl>
uniform vec2 uSize;
uniform vec4 uColor;
out vec4 fragColor;
void main() {
vec2 coo = FlutterFragCoord().xy/uSize;
vec4 black = vec4(0,0.0,0.0,1.0);
fragColor = mix(black,uColor,coo.x);
}
在 Flutter 中也是经过 setFloat 传入各个重量的值,索引次序按照GLSL 代码中变量界说的次序。其中索引 0,1
表明尺度的二维向量;那么 2,3,4,5
便是表明色彩的四个重量:
---->[lib/paint/shaders/var_demos/v2_painter.dart]----
class V2ShaderPainter extends CustomPainter {
V2ShaderPainter({required this.shader, required this.color});
FragmentShader shader;
Color color;
@override
void paint(Canvas canvas, Size size) {
shader.setFloat(0, size.width);
shader.setFloat(1, size.height);
shader.setFloat(2, color.red / 255);
shader.setFloat(3, color.green / 255);
shader.setFloat(4, color.blue / 255);
shader.setFloat(5, color.alpha / 255);
final paint = Paint()..shader = shader;
canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height),
paint,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
3. 纹路图片传参
下面来看一下怎么 Flutter 中怎么将一张图片数据作为入参传递为着色器代码,比方把一张可爱女孩的相片展现到屏幕上:
着色器代码中,经过 uniform
声明 sampler2D
类型的目标表明贴图变量,经过内置的 texture
函数可以依据坐标值提取纹路的色彩;假如将其作为输出色,即可将图片原封不动地展现出来:
---->[shaders/var_03.frag]----
#version 460 core
precision mediump float;
#include <flutter/runtime_effect.glsl>
uniform vec2 uSize;
uniform sampler2D uTexture;
out vec4 fragColor;
void main() {
vec2 coo = FlutterFragCoord().xy/uSize;
vec4 color = texture(uTexture, coo);
fragColor = color;
}
在 Flutter 中也是经过 setImageSampler 传入 ui.Image
目标作为贴图的数据,索引次序从 0 开端,假如由多张图片,依次计数。
---->[lib/paint/shaders/var_demos/v3_painter.dart]----
class V3ShaderPainter extends CustomPainter {
V3ShaderPainter({required this.shader, required this.image});
FragmentShader shader;
ui.Image image;
@override
void paint(Canvas canvas, Size size) {
shader.setFloat(0, size.width);
shader.setFloat(1, size.height);
/// 传递贴图数据
shader.setImageSampler(0, image);
final paint = Paint()..shader = shader;
canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height),
paint,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
4. 归纳传参事例
最终经过一个归纳小事例练习一下传参:既然 GLSL 代码中可以获得纹路图片的每个像素色彩。那么就可以经过 mix 函数 将像素色彩和另一个色彩混合
。
如下所示,挑选色彩时进行混色;下方的进度条用于设置混色的程度,依据程度进行插值核算,视觉表现便是程度越大,混色越 “浓” 。
切换混色 | 调整混色程度 |
---|---|
下面是着色器代码,界说程度 progress、尺度 uSize、色彩 uColor、贴图 uTexture 四个变量:
---->[shaders/var_04.frag]----
#version 460 core
precision mediump float;
#include <flutter/runtime_effect.glsl>
uniform float progress;
uniform vec2 uSize;
uniform vec4 uColor;
uniform sampler2D uTexture;
out vec4 fragColor;
void main() {
vec2 coo = FlutterFragCoord().xy / uSize;
vec4 color = texture(uTexture, coo);
fragColor = mix(color, uColor, progress);
}
Flutter 中经过交互更新数据,并设置对应的数据传递给着色器代码,留意参数的索引次序要对应好:
---->[lib/paint/shaders/var_demos/v4_painter.dart]----
class V4ShaderPainter extends CustomPainter {
V4ShaderPainter({
required this.shader,
required this.image,
required this.color,
required this.progress,
});
FragmentShader shader;
ui.Image image;
Color color;
double progress;
@override
void paint(Canvas canvas, Size size) {
shader.setFloat(0, progress);
shader.setFloat(1, size.width);
shader.setFloat(2, size.height);
shader.setFloat(3, color.red / 255);
shader.setFloat(4, color.green / 255);
shader.setFloat(5, color.blue / 255);
shader.setFloat(6, color.alpha / 255);
shader.setImageSampler(0, image);
final paint = Paint()..shader = shader;
canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height),
paint,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
总得来说,Flutter 像着色器代码传递参数还是十分方便的,有了参数的加持,Flutter 就可以在交互过程中完成很多实用的功用,比方图片的特效处理,艳丽图片的生成。那本篇就到这儿,感谢观看,下次再会~