Flutter & GLSL - 叁 | 变量传参

Flutter & GLSL 系列文章:


1. 从尺寸入参开始说起

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

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


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

c 复制代码
---->[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 设置高度:

dart 复制代码
---->[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

对于多维的值,就是各个分量的混合值。

c 复制代码
---->[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 就是表示颜色的四个分量:

dart 复制代码
---->[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 函数可以根据坐标值提取纹理的颜色;如果将其作为输出色,即可将图片原封不动地展示出来:

c 复制代码
---->[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 开始,如果由多张图片,依次计数。

dart 复制代码
---->[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 四个变量:

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

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

相关推荐
砖厂小工2 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心3 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心3 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
shankss4 小时前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
Kapaseker5 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴6 小时前
Android17 为什么重写 MessageQueue
android
忆江南20 小时前
iOS 深度解析
flutter·ios
明君8799721 小时前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter
恋猫de小郭1 天前
移动端开发稳了?AI 目前还无法取代客户端开发,小红书的论文告诉你数据
前端·flutter·ai编程
MakeZero1 天前
Flutter那些事-交互式组件
flutter