`UniformsSetter.setColor` sends every color to the shader as black
Los mantenedores suelen responder en 1 día
Evaluación
Este issue todavía no se ha evaluado.
Descripción
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
- Lenguaje dominante
- Dart
- Estrellas
- 10.8k
- Forks
- 1.1k
- Merge medio
- 1 d 22 h
- PR fusionados (30 d)
- 54
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de flame-engine/flame
-
`Viewfinder.visibleGameSize` is not reapplied when the viewport changes sizePosiblemente ocupada @ufrshubham la tomó hace 4 días. Abiertobug
flame-engine/flame#4204 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
`Viewfinder.visibleGameSize` zooms in too far with a `FixedResolutionViewport`Posiblemente ocupada @ufrshubham la tomó hace 4 días. Abiertobug
flame-engine/flame#4203 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Camera bounds with `considerViewport: true` are not updated when the viewport is resizedPosiblemente ocupada @ufrshubham la tomó hace 4 días. Abiertobug
flame-engine/flame#4202 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
`CameraComponent.setBounds` with `considerViewport: true` only works for bounds that start at (0, 0), at zoom 1Posiblemente ocupada @ufrshubham la tomó hace 4 días. Abiertobug
flame-engine/flame#4201 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
`findPostProcessFromContext` returns `null` for components rendered with `CameraComponent.postProcess`Posiblemente ocupada @ufrshubham la tomó hace 4 días. Abiertobug
flame-engine/flame#4200 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de flame-engine/flame
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
customerio/customerio-flutter#441 ·
Los mantenedores suelen responder en 2 días
-
JsonParseNode.getObjectValue returns non-null model on null JSON nodes and duplicates lifecycle hooksPosiblemente ocupada @hardikkaurani la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
microsoft/kiota-dart#170 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
fable-compiler/Fable#5280 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
immich-app/immich#33043 ·
Los mantenedores suelen responder en 1 día
-
[FR] 希望能折叠搜索历史Abiertoenhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
bggRGjQaUbCoE/PiliPlus#3503 ·
Los mantenedores suelen responder en 1 día