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

white outlines around sprite #5266

Closed
BKSalman opened this issue Jul 10, 2022 · 23 comments
Closed

white outlines around sprite #5266

BKSalman opened this issue Jul 10, 2022 · 23 comments
Labels
A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior

Comments

@BKSalman
Copy link

BKSalman commented Jul 10, 2022

Bevy version

main branch 518408d

Windows 10

AdapterInfo { name: "NVIDIA GeForce GTX 1070 Ti", vendor: 4318, device: 7042, device_type: DiscreteGpu, backend: Vulkan }

What went wrong

I was using the 0.7 release and the sprite was loading fine, but when I switched to the main branch 518408d it shows white outline around the sprite

image

this is how I'm spawning the sprite

let texture: Handle<Image> = asset_server.load("Hand.png");

    let sprite = Sprite{
        anchor: bevy::sprite::Anchor::TopLeft,
        ..Default::default()
    };

    commands.spawn_bundle(SpriteBundle{
        sprite,
        texture,
        ..Default::default()
    })
    .insert(Name::new("Hand"))
    .insert(Transform::from_xyz(0., 0., 0.))
    .insert(ClearColor(Color::NONE))
    .insert(Hand{
        x_offset: -13.,
        y_offset: 35.,
        ..Default::default()
    });
@BKSalman BKSalman added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Jul 10, 2022
@alice-i-cecile alice-i-cecile added A-Rendering Drawing game state to the screen and removed S-Needs-Triage This issue needs to be labelled labels Jul 10, 2022
@alice-i-cecile
Copy link
Member

Like the result of the new anti-aliasing changes. @superdump @aevyrie do either of you recall exactly which PR this was?

@BKSalman
Copy link
Author

BKSalman commented Jul 10, 2022

Like the result of the new anti-aliasing changes.

Yes I think so, I tried to change the Mssa samples, but didn't seem to fix it

@aevyrie
Copy link
Member

aevyrie commented Jul 10, 2022

Try changing image filtering to nearest

@BKSalman
Copy link
Author

I added this

.insert_resource(FilterMode::Nearest)

but it doesn't seem to be fixing it

@aevyrie
Copy link
Member

aevyrie commented Jul 11, 2022

Could you try switching ClearColor(Color::NONE) to Color::BLACK? As a component, ClearColor isn't doing anything there?

Yes I think so, I tried to change the Mssa samples, but didn't seem to fix it

Just to confirm, did you try removing/commenting out the Msaa resource completely?

@superdump
Copy link
Contributor

ClearColor as a component on an entity with a SpriteBundle looks odd to me

@irate-devil
Copy link
Contributor

FilterMode is not used as a resource. You can use the ImageSettings resource to set a default FilterMode.

Resource trait and derive, when?

@alice-i-cecile
Copy link
Member

This is a good example in the wild of why #5007 is helpful :)

@BKSalman
Copy link
Author

Just to confirm, did you try removing/commenting out the Msaa resource completely?

Yes

@BKSalman
Copy link
Author

ClearColor as a component on an entity with a SpriteBundle looks odd to me

Yes, I was just testing, I removed it later, sorry for not clarifying

@BKSalman
Copy link
Author

FilterMode is not used as a resource. You can use the ImageSettings resource to set a default FilterMode.

this solved the white outlines issue thanks!

however the image is looking a bit pixelated while moving if that makes sense...

ezgif.com-gif-maker.1.mp4

@irate-devil
Copy link
Contributor

@alice-i-cecile I don't think this should be closed.
Setting FilterMode to nearest does solve the issue of a white outline but that's not really a solution.
As you can see in the video above, nearest filtering (as expected) gives terrible aliasing.

@aevyrie Do you have any idea what is causing this, and how it should be fixed? (Asking since you suggested using nearest.)

This really should be fixed before 0.8.

@superdump
Copy link
Contributor

I agree, this shouldn't be closed yet.

@BKSalman can you share the sprite and an small example that shows the original issue? I'd like to investigate it and it will be quicker if I can reproduce the problem locally.

@superdump superdump reopened this Jul 12, 2022
@aevyrie
Copy link
Member

aevyrie commented Jul 12, 2022

I've seen this before and I think it's a bug with how alpha is sampled and blended. I would've thought this would be handled automagically seeing as we don't define the behavior of samplers (?).

@superdump pretty sure I've seen this when linearly sampling an image with transparency so I don't think it's unique to this particular image. The border from none to some alpha turns white instead of the alpha component being used. We should make a test image that has an alpha gradient.

@superdump
Copy link
Contributor

I grabbed the khronos gltf alpha blend test texture from here: https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/AlphaBlendModeTest/glTF/AlphaBlendLabels.png

It has a blue and yellow striped region which fades from alpha = 1 at the bottom to alpha = 0 at the top. That region has an opaque black border around it. It exhibits a similar issue:
Screenshot 2022-07-12 at 19 39 24

From that I guess that the alpha value of the opaque pixel immediately adjacent to the transparent pixels is being bilinearly-interpolated between close to 0 and 1, so significantly increasing the alpha, and blending the saturated stripe colours with black to create the outline.

I inspected this in an Xcode GPU frame capture. The following three images show the pixel to the left that is sampled from four surrounding transparent pixels, then from a mix of 2 transparent pixels and 2 opaque pixels, resulting in the bad-looking coloured pixels, then four opaque pixels:
Screenshot 2022-07-12 at 19 40 02
Screenshot 2022-07-12 at 19 40 09
Screenshot 2022-07-12 at 19 40 18

I'm not sure how to solve this aside from not having opaque pixels next to transparent pixels of a contrasting colour. Maybe someone has a good idea or maybe I'll come up with one. I'll let it stew for a bit.

@aevyrie
Copy link
Member

aevyrie commented Jul 12, 2022

@BKSalman
Copy link
Author

BKSalman commented Jul 13, 2022

sorry for not replying quickly ( that's the least I could do to help this awesome engine! )

you can check all of the source code in this repo, it's a pretty small and simple project should be really easy to navigate through + it has the images used

@CptPotato
Copy link
Contributor

CptPotato commented Jul 13, 2022

I can't tell for sure but it very much looks like this happens due to linear filtering at the edge (transition between opaque pixels and transparent). If that's not the cause ignore this comment 👀

The alpha is linearly interpolated from 255 to 0 and the color is interpolated from black to white. "In between" there are values which are semi-transparent greys that are only hit when the image gets resampled (i.e. doesn't perfectly align with the pixels of the output resolution). Generation of mipmaps would have a similar effect.

As you can see, removing the alpha channel reveals that the image's transparent pixels are white:
image

I think this is a bit tricky because to some extent this is the "correct" behavior in my opinion when it comes to the underlying linear interpolation.

This issue can be worked around with at least these methods, though:

  • Manually perform interpolation taking the alpha channel into account
    • lose out on performance (no hardware filtering)
  • fixup the image's color around the borders to be the same as the borders (or fill the background entirely)
    • I did this with the Hand.png asset if you want to check:

      .

@aevyrie
Copy link
Member

aevyrie commented Jul 13, 2022

@CptPotato I'm inclined to agree this is an asset issue. The linear filter is behaving as expected in this case, interpolating between white transparent and black opaque.

@CptPotato
Copy link
Contributor

For reference, Godot has an asset import option that addresses this:
https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_images.html#fix-alpha-border

A similar feature might be useful once bevy has it's own asset pipeline.

@Galm007
Copy link

Galm007 commented Jul 22, 2022

I also have this problem but it seems that the ImageSettings resource is not available anymore. I checked in docs.rs but ImageSettings is also not listed there. Is there a new solution to this issue?

@aevyrie
Copy link
Member

aevyrie commented Jul 22, 2022

I also have this problem but it seems that the ImageSettings resource is not available anymore. I checked in docs.rs but ImageSettings is also not listed there. Is there a new solution to this issue?

It is in bevy main, not yet released to crates.io. You can see it in the dev docs here: https://dev-docs.bevyengine.org/bevy/render/texture/struct.ImageSettings.html

@Ahuang0107
Copy link

I have the same problem when I working with bevy 0.8.0, and add app.insert_resource(ImageSettings::default_nearest()) before DefaultPlugins does solved my problem.

but at the same time, I find that bevy having different behaviour on different GPU divice with this issue, it seems that only show on Integrated GPU device. I have tried three different GPU divice,

  • the sprite was loading fine when I work on windows 10 AdapterInfo { name: "NVIDIA GeForce GTX 960", vendor: 4318, device: 5121, device_type: DiscreteGpu, backend: Vulkan }

  • but shows white outline when I work on MacOS (sorry I can't offer this AdapterInfo right now because the device is not at hand, it is M1 Pro, I will update the AdapterInfo later )
    and windows 10 AdapterInfo { name: "Intel(R) UHD Graphics 630", vendor: 32902, device: 16024, device_type:IntegratedGpu, backend: Vulkan }

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 C-Bug An unexpected or incorrect behavior
Projects
None yet
Development

No branches or pull requests

8 participants