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

オープン
#4,211 コメント 0 件 リアクション 0 件 担当者 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日 22時間
マージ済み PR(30日)
54

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

flame-engine/flame のほかの issue

flame-engine/flame の issue をすべて見る

似ている issue

Dart の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。