bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.51k stars 3.6k forks source link

white outlines around sprite #5266

Closed BKSalman closed 2 years ago

BKSalman commented 2 years ago

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()
    });
alice-i-cecile commented 2 years ago

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

BKSalman commented 2 years ago

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 commented 2 years ago

Try changing image filtering to nearest

BKSalman commented 2 years ago

I added this

.insert_resource(FilterMode::Nearest)

but it doesn't seem to be fixing it

aevyrie commented 2 years ago

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 commented 2 years ago

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

tim-blackbird commented 2 years ago

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 commented 2 years ago

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

BKSalman commented 2 years ago

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

Yes

BKSalman commented 2 years ago

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 commented 2 years ago

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

https://user-images.githubusercontent.com/85521119/178406752-2c3b7a62-0522-48be-abaf-772a13f3f63f.mp4

tim-blackbird commented 2 years ago

@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 commented 2 years ago

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.

aevyrie commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

This looks like the same issue, with solutions: https://stackoverflow.com/questions/45742828/opengl-blending-creates-white-border-around-textures

BKSalman commented 2 years ago

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 commented 2 years ago

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 :eyes:

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:

aevyrie commented 2 years ago

@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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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,