`UniformsSetter.setColor` sends every color to the shader as black

未关闭
#4,211 0 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

维护者通常 1 天内回复

@ufrshubham 已经在做这个了。

开始于 2026年11月5日。

  • #4212 来自 @ufrshubham —— 未关闭

评估

这个 Issue 还没有评估数据。

描述

bug
What happened?

UniformsSetter.setColor (and therefore setColors) sends every color to the shader as black. It divides each channel by 255 (fragment_shader.dart#L91-L93):

setFloat(color.r / 255 * multiplier);
setFloat(color.g / 255 * multiplier);
setFloat(color.b / 255 * multiplier);
setFloat(color.a);

Color.r, Color.g and Color.b are already doubles between 0 and 1, so the shader receives values between 0 and about 0.004. For example, Colors.red arrives as vec4(0.0037, 0.0010, 0.0008, 1.0) instead of vec4(0.957, 0.263, 0.212, 1.0). Alpha isn't divided, so the result is an opaque black and not a transparent color. With premultiply: true the channels are also multiplied by alpha, but they are still divided by 255 first.

The code was added in #3578, which copied the extension from flutter_shaders. There it uses color.red / 255, which is correct because the deprecated Color.red getter is an int between 0 and 255. While copying, .red/.green/.blue/.opacity were replaced with .r/.g/.b/.a, but the / 255 was kept. Nothing in the repository calls setColor, and it has no tests, so this went unnoticed. The basic shader tutorial passes its color with setVector(color.toVector4()), which is why it isn't affected.

The bug is in the released versions, including flame-v1.38.2 and flame-v2.0.0-dev.1.

Image
What do you expect?

setColor sends the channels of the color unchanged, so setColor(Colors.red) sets the uniform to vec4(0.957, 0.263, 0.212, 1.0), as the dartdoc of setFloatUniforms describes. With premultiply: true, the red, green and blue channels are multiplied by alpha.

How can we reproduce this?
  1. Create a fragment shader that outputs a uniform vec4 uColor.
  2. Set that uniform with shader.setFloatUniforms((u) => u.setColor(Colors.red)).
  3. Draw a rect with Paint()..shader = shader.
  4. The rect is black. Setting the same uniform with u.setFloats([color.r, color.g, color.b, color.a]) draws it red.

The example below renders the same shader twice for each color: the left column uses setColor and the right column uses setFloats.

What steps should take to fix this?

Remove the / 255 from the three channels in setColor:

setFloat(color.r * multiplier);
setFloat(color.g * multiplier);
setFloat(color.b * multiplier);
setFloat(color.a);

With that change, both columns in the example below are identical.

Do have an example of where the bug occurs?

shaders/swatch.frag:

#version 460 core

uniform vec4 uColor;

out vec4 fragColor;

void main() {
  fragColor = vec4(uColor.rgb * uColor.a, uColor.a);
}

pubspec.yaml:

flutter:
  shaders:
    - shaders/swatch.frag

lib/main.dart:

import 'dart:ui';

import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart' hide Image;

void main() {
  runApp(GameWidget(game: SetColorBugGame()));
}

/// Renders the same shader twice per color: the left column uses
/// `setColor`, the right column passes the channels with `setFloats`.
class SetColorBugGame extends FlameGame {
  static const colors = <(String, Color)>[
    ('Colors.red', Colors.red),
    ('Colors.purpleAccent', Colors.purpleAccent),
    ('Colors.amber', Colors.amber),
    ('Colors.white', Colors.white),
  ];

  @override
  Color backgroundColor() => const Color(0xFF2B3340);

  @override
  Future<void> onLoad() async {
    final program = await FragmentProgram.fromAsset('shaders/swatch.frag');

    final header = TextPaint(
      style: const TextStyle(fontSize: 20, color: Colors.white),
    );
    final label = TextPaint(
      style: const TextStyle(fontSize: 14, color: Color(0xFFB8C2D0)),
    );

    const swatch = 110.0;
    const colX = [220.0, 420.0];
    const top = 70.0;

    add(TextComponent(text: 'setColor(color)', textRenderer: header)
      ..position = Vector2(colX[0] + swatch / 2, 24)
      ..anchor = Anchor.topCenter);
    add(TextComponent(text: 'setFloats([r, g, b, a])', textRenderer: header)
      ..position = Vector2(colX[1] + swatch / 2, 24)
      ..anchor = Anchor.topCenter);

    for (var i = 0; i < colors.length; i++) {
      final (name, color) = colors[i];
      final y = top + i * (swatch + 16);
      add(TextComponent(text: name, textRenderer: label)
        ..position = Vector2(colX[0] - 16, y + swatch / 2)
        ..anchor = Anchor.centerRight);
      add(ShaderSwatch(program, color, useSetColor: true)
        ..position = Vector2(colX[0], y)
        ..size = Vector2.all(swatch));
      add(ShaderSwatch(program, color, useSetColor: false)
        ..position = Vector2(colX[1], y)
        ..size = Vector2.all(swatch));
    }
  }
}

class ShaderSwatch extends PositionComponent {
  ShaderSwatch(FragmentProgram program, this.color, {required this.useSetColor})
    : shader = program.fragmentShader();

  final FragmentShader shader;
  final Color color;
  final bool useSetColor;
  late final Paint _paint = Paint()..shader = shader;

  @override
  void render(Canvas canvas) {
    shader.setFloatUniforms((u) {
      if (useSetColor) {
        u.setColor(color);
      } else {
        u.setFloats([color.r, color.g, color.b, color.a]);
      }
    });
    canvas.drawRect(size.toRect(), _paint);
  }
}
Relevant log output
# Nothing is logged or thrown, the colors are only rendered as black.
Execute in a terminal and put output into the code block below
[✓] Flutter (Channel stable, 3.47.9, on Linux Mint 22.3 7.0.0-42-generic, locale en_IN)
    • Flutter version 3.47.9 on channel stable
    • Framework revision c197ca5199 (7 days ago), 2026-10-29 10:10:20 -0400
    • Engine revision 89c39edfdf
    • Dart version 3.13.7
    • DevTools version 2.60.0

[✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.

[✓] Linux toolchain - develop for Linux desktop
    • Ubuntu clang version 18.1.3 (1ubuntu1)
    • cmake version 3.28.3
    • ninja version 1.11.1
    • pkg-config version 1.8.1
    • OpenGL core renderer: Mesa Intel(R) HD Graphics 4000 (IVB GT2) (X11)
    • OpenGL ES renderer: Mesa Intel(R) HD Graphics 4000 (IVB GT2) (X11)

[✓] Connected device (1 available)
[✓] Network resources

! Doctor found issues in 1 category.
Affected platforms

All

Other information

The bug is in Dart code that runs before anything reaches the GPU, so it doesn't depend on the platform or renderer. The screenshots are from Linux with Impeller (OpenGLES).

Are you interested in working on a PR for this?
  • I want to work on this
主要语言
Dart
星标
10.8k
派生
1.1k
平均合并
1 天 19 小时
30 天内合并 PR
54

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

flame-engine/flame 的其他 Issue

查看 flame-engine/flame 的全部 Issue

相似的 Issue

更多 Dart Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。