`UniformsSetter.setColor` sends every color to the shader as black
メンテナーはふだん 1 日以内に返信
評価
この issue はまだ評価されていません。
説明
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.
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?
- Create a fragment shader that outputs a
uniform vec4 uColor. - Set that uniform with
shader.setFloatUniforms((u) => u.setColor(Colors.red)). - Draw a rect with
Paint()..shader = shader. - 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
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
flame-engine/flame のほかの issue
-
`Viewfinder.visibleGameSize` is not reapplied when the viewport changes size対応中かも @ufrshubham が 4 日前に担当しました。 オープンbug
flame-engine/flame#4204 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
`Viewfinder.visibleGameSize` zooms in too far with a `FixedResolutionViewport`対応中かも @ufrshubham が 4 日前に担当しました。 オープンbug
flame-engine/flame#4203 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
Camera bounds with `considerViewport: true` are not updated when the viewport is resized対応中かも @ufrshubham が 4 日前に担当しました。 オープンbug
flame-engine/flame#4202 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
`CameraComponent.setBounds` with `considerViewport: true` only works for bounds that start at (0, 0), at zoom 1対応中かも @ufrshubham が 4 日前に担当しました。 オープンbug
flame-engine/flame#4201 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
`findPostProcessFromContext` returns `null` for components rendered with `CameraComponent.postProcess`対応中かも @ufrshubham が 4 日前に担当しました。 オープンbug
flame-engine/flame#4200 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
flame-engine/flame の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
customerio/customerio-flutter#441 ·
メンテナーはふだん 2 日以内に返信
-
JsonParseNode.getObjectValue returns non-null model on null JSON nodes and duplicates lifecycle hooks対応中かも @hardikkaurani が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
microsoft/kiota-dart#170 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
fable-compiler/Fable#5280 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
immich-app/immich#33043 ·
メンテナーはふだん 1 日以内に返信
-
[FR] 希望能折叠搜索历史オープンenhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
bggRGjQaUbCoE/PiliPlus#3503 ·
メンテナーはふだん 1 日以内に返信