Open janikrabe opened 3 years ago
I hit this issue in Bevy 0.10 during Bevy Game Jam 3. We made a 2D pixel art game with scaled up pixels via camera zoom, and then I tried to add nametags to the player / enemies. I ended up working around this issue by rendering the nametag text with an absurd font size (EDIT: This was actually unnecessary), then scaling the text entity down. This was good enough for the jam, but it was a rough hack, the text still had artifacts due to the scaling (EDIT: Wrong), and it was an extra performance cost (EDIT: Only because I incorrectly used a larger font size).
In retrospect we should have scaled our sprites up instead of zooming the camera in, but it's unfortunate that camera zoom is effectively incompatible with world-space text.
This is effecting me on a current project in 0.11, I'd be happy to work on trying to fix it if someone with a bit more experience in the engine could point me in the right direction
This was good enough for the jam, but it was a rough hack, the text still had artifacts due to the scaling, and it was an extra performance cost.
Can you elaborate on how this didn't work out for you / what artifacts you were experiencing?
font_size
is the vertical height in pixels that the text is rasterized at. If you place that font texture into the world and zoom in 10x, it will appear pixelized. If you need the text to appear crisp at a height of 250 screen pixels, then it needs to be rasterized at 250px. There shouldn't be an "extra" performance cost.
(side note: font_size
docs were improved recently in #9320 and #9524 is seeking to add viewport-based controls for font size, although I don't think that it would particularly help when the projection is scaled.)
I updated the repro for bevy 0.11 here: https://gist.github.com/rparrett/c9a485a313235d678dffa30f1bcba974
Can you elaborate on how this didn't work out for you / what artifacts you were experiencing?
The text gets pixelated first, and then scaled down via linear sampling, not pixel-perfect sampling. That's what causes the artifacts. The performance cost is due to choosing a huge font size to reduce (but not eliminate) the artifacts from linear scaling.
The above is totally wrong. The correct workaround is to simply have a system scale the text entity's transform by the inverse of the camera zoom (4x zoom => scale by 0.25).
Font textures can be configured to use nearest sampling, although the easiest way to accomplish that is to set the default sampler globally. But that's probably the right call if you need it for text.
For some reason I remember there being more to the story at the time, but going back and looking at the actual font size rendered + the actual pixels on screen (visually comparing to the same font rendered in my browser on Google Fonts), everything seems fine. The only difference is Google Fonts does subpixel anti-aliasing whereas bevy just does normal anti-aliasing, which makes a big difference for smaller text, but that's not related to this issue.
This issue appears to persist with Bevy 0.12.
Reproduction code: https://github.com/TimJentzsch/bevy_triage/tree/main/issues/issue_1890
Font size 250 with camera projection scale 1.0:
Font size 25 with camera projection scale 0.1:
Notably, this issue only happens with Text2dBundle
, not with TextBundle
(from UI).
The latter ignores the camera scale and uses UiScale
instead, which handles this better.
Workaround: Increase the font_size
and set Transform#scale
to a smaller value.
let mut bundle = Text2dBundle {
transform: transform.with_scale(Vec3::splat(0.1)),
..Default::default()
};
bundle.text.sections.push(TextSection {
value: format!("test"),
style: TextStyle {
//...
font_size: font_size * 10,
..Default::default(),
},
});
You only need to scale the text's transform by the inverse of the camera zoom. No need to touch the font size.
Bevy version
Operating system & version
Linux/Wayland
What you did
Modify the camera in the
text2d
example to use a scale of0.1
:What you expected to happen
The text should rasterize properly with no visible artifacts.
What actually happened
The text is rasterized to 10x10 pixel squares (1/scale).
Additional information
This can also be seen with
ScalingMode::FixedHorizontal
when the scale does not match the window's width:(The same applies to
ScalingMode::FixedVertical
, of course.)