Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Text rasterization is based on world units instead of pixels #1890

Open
janikrabe opened this issue Apr 11, 2021 · 11 comments
Open

Text rasterization is based on world units instead of pixels #1890

janikrabe opened this issue Apr 11, 2021 · 11 comments
Labels
A-Rendering Drawing game state to the screen A-Text Rendering and layout for characters A-UI Graphical user interfaces, styles, layouts, and widgets C-Bug An unexpected or incorrect behavior

Comments

@janikrabe
Copy link

janikrabe commented Apr 11, 2021

Bevy version

  • v0.5.0
  • v0.6.0
  • v0.7.0

Operating system & version

Linux/Wayland

What you did

Modify the camera in the text2d example to use a scale of 0.1:

use bevy::prelude::*;
use bevy::render::camera::{DepthCalculation, OrthographicProjection};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup)
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands
        .spawn_bundle(OrthographicCameraBundle::new_2d())
        .insert(OrthographicProjection {
            near: 0.0,
            far: 1000.0,
            scale: 0.1,
            depth_calculation: DepthCalculation::ZDifference,
            ..Default::default()
        });
    commands.spawn_bundle(Text2dBundle {
        text: Text::with_section(
            "Hello.",
            TextStyle {
                font: asset_server.load("fonts/FiraSans-Bold.ttf"),
                font_size: 25.0,
                color: Color::WHITE,
            },
            TextAlignment {
                vertical: VerticalAlign::Center,
                horizontal: HorizontalAlign::Center,
            },
        ),
        ..Default::default()
    });
}

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).

raster

Additional information

This can also be seen with ScalingMode::FixedHorizontal when the scale does not match the window's width:

// ...
.insert(OrthographicProjection {
    near: 0.0,
    far: 1000.0,
    window_origin: WindowOrigin::Center,
    scaling_mode: ScalingMode::FixedHorizontal,
    scale: 100.0,
    depth_calculation: DepthCalculation::ZDifference,
    ..Default::default()
});
// ...

(The same applies to ScalingMode::FixedVertical, of course.)

@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior A-UI Graphical user interfaces, styles, layouts, and widgets A-Rendering Drawing game state to the screen labels Apr 11, 2021
@janikrabe janikrabe changed the title Incorrect text rasterization with non-pixel-sized units Text rasterization is based on world coordinates instead of pixels Dec 31, 2021
@janikrabe janikrabe changed the title Text rasterization is based on world coordinates instead of pixels Text rasterization is based on world units instead of pixels Dec 31, 2021
@benfrankel
Copy link
Contributor

benfrankel commented Jun 6, 2023

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.

@LLBlumire
Copy link

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

@rparrett
Copy link
Contributor

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

@benfrankel
Copy link
Contributor

benfrankel commented Sep 11, 2023

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).

@rparrett
Copy link
Contributor

rparrett commented Sep 12, 2023

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.

@benfrankel
Copy link
Contributor

benfrankel commented Sep 12, 2023

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.

@TimJentzsch
Copy link
Contributor

Triage

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:
Crisp text

Font size 25 with camera projection scale 0.1:
Extremely blurry text with pixelated artifacts

@TimJentzsch
Copy link
Contributor

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.

@jjyr
Copy link

jjyr commented Feb 19, 2024

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(),
        },
    });

@benfrankel
Copy link
Contributor

You only need to scale the text's transform by the inverse of the camera zoom. No need to touch the font size.

@tbillington
Copy link
Contributor

That would depend on if you want the text to scale up when zoomed in. If you want that behaviour then changing the font size is necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen A-Text Rendering and layout for characters A-UI Graphical user interfaces, styles, layouts, and widgets C-Bug An unexpected or incorrect behavior
Projects
None yet
Development

No branches or pull requests

9 participants