Flutter & GLSL - 叁 | 变量传参

本文介绍了如何在Flutter中通过GLSL着色器动态传递参数,如尺寸、颜色和纹理图片,展示了从简单尺寸变量到渐变颜色和图片混合的实例,展示了Flutter的强大交互能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


theme: cyanosis

Flutter & GLSL 系列文章:

shader2.png


1. 从尺寸入参开始说起

上一篇介绍了,在着色器中坐标和颜色的关系,将坐标归 1 后留下一个问题:

如何让着色器代码中的 size 不写死,由外界传递呢?

image.png


在着色器代码中,可以通过 uniform 定义 vec2 类型变量 uSize:

```c ---->[shaders/var_01.frag]----

version 460 core

precision mediump float;

include

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 设置高度:

```dart ---->[lib/paint/shaders/vardemos/v1painter.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. 选择颜色进行渐变

下面再通过一个案例熟悉一下入参的处理,我们在下方准备了一些备选色,现在的需求是

将选择的颜色作为入参,通过着色器展示 黑色 → 选中色 的渐变效果:

41.gif

道理是一样的,颜色是一个四维向量 vec4,作为参数传入着色器程序。定义 uniform vec4 uColor; 然后通过 mix 函数将黑色和传入颜色,根据像素的横坐标进行混合。

mix 是一个内置函数,由三个入参 a,b,t 。表示用于在 a, b 个值在 t 分度时的线性混合。
举个小例子:8 和 24 在 0.4 处的混合值是 8 + (24 -8)*0.4
对于多维的值,就是各个分量的混合值。

```c ---->[shaders/var_02.frag]----

version 460 core

precision mediump float;

include

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 就是表示颜色的四个分量: ```dart ---->[lib/paint/shaders/vardemos/v2painter.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 中如何将一张图片数据作为入参传递为着色器代码,比如把一张可爱女孩的照片展示到屏幕上:

image.png

着色器代码中,通过 uniform 声明 sampler2D 类型的对象表示贴图变量,通过内置的 texture 函数可以根据坐标值提取纹理的颜色;如果将其作为输出色,即可将图片原封不动地展示出来:

```c ---->[shaders/var_03.frag]----

version 460 core

precision mediump float;

include

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 开始,如果由多张图片,依次计数。

```dart ---->[lib/paint/shaders/vardemos/v3painter.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 函数 将像素颜色和另一个颜色混合
如下所示,选择颜色时进行混色;下方的进度条用于设置混色的程度,根据程度进行插值计算,视觉表现就是程度越大,混色越 "浓" 。

| 切换混色 | 调整混色程度 | | --- | --- | | 42.gif | 44.gif |

下面是着色器代码,定义程度 progress、尺寸 uSize、颜色 uColor、贴图 uTexture 四个变量:

```c ---->[shaders/var_04.frag]----

version 460 core

precision mediump float;

include

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 中通过交互更新数据,并设置对应的数据传递给着色器代码,注意参数的索引顺序要对应好:

```dart ---->[lib/paint/shaders/vardemos/v4painter.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 就可以在交互过程中完成很多实用的功能,比如图片的特效处理,绚丽图片的生成。那本篇就到这里,感谢观看,下次再见~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值