bevyengine / bevy

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

Blend modes for 2D sprite bundles #9314

Open IDEDARY opened 1 year ago

IDEDARY commented 1 year ago

2D BLEND MODES

Add blend modes to 2D sprite bundle.

Blend modes is a crucial 2D functionality that allows implementation of several important VFX for 2D. The most prominent one is "fake sprite lighting", where you use grayscale sprites for imitating shadows and light in your game.

This method is very common and very cheap for computing power.

image

Here is list of all blend modes in Aseprite:

image

What to add

I would suggest implementing these 5 primary blend modes:

Difficulty

Switching between blend modes on GPU might be be rather slow process and we want to minimize that. We would need to somehow sort and group entities with same blend modes together, so they can be rendered in one batch. But that might fight with depth sorting.

But I am not knowledgeable with shader programming much, so I cannot say for sure. That's just my observation from other engines.

Usability

I propose creating new component that will define how texture will be rendered. We can then create enum and add another field to SpriteBundle.

pub enum BlendMode {
    Normal,
    Lighten,
    Darken,
    Addition,
    Subtraction,
    Overlay,
}

pub struct SpriteBundle {
    pub sprite: Sprite,
    pub transform: Transform,
    pub blend_mode: BlendMode,    // Add this new component
    pub global_transform: GlobalTransform],
    pub texture: Handle<Image>,
    pub visibility: Visibility,
    pub computed_visibility: ComputedVisibility,
}
nicopap commented 1 year ago
jabuwu commented 1 year ago

I was working on adding this, but I ultimately decided against it after observing what other game engines do.

To do this without a unique material per blend mode (which is already possible, btw), it will need to be supported by render batching. In order for batching to support this, blend modes will need to be added to the Mesh2dPipelineKey. Supporting all of those blend modes in aseprite is basically out of the question, due to the limited bits available in this key, so it would need to be only a subset. People might disagree on which blend modes should be supported (I want Screen rather than Overlay, myself). Even then, the blend modes will be different if you're using premultiplied alpha.

The exception to this in other engines is particles, which do seem to provide this at the batching level.

But, this is just my understanding, and I could have some things wrong.

nicopap commented 1 year ago

The (initial) set of blend mode would probably look like what we already do in the 3D renderer: https://docs.rs/bevy/latest/bevy/pbr/enum.AlphaMode.html

jabuwu commented 1 year ago

The (initial) set of blend mode would probably look like what we already do in the 3D renderer:

Blend modes in the 3D renderer were accomplished, in part, by adding blend modes to the 3D pipeline key.

https://github.com/bevyengine/bevy/blob/a024a1f3b99ff9040fe3af5c7c1d8ef9a5434925/crates/bevy_pbr/src/render/mesh.rs#L710-L714

The reason I didn't pursue the issue further in 2D is because of this space limitation. You'll have to make tradeoffs somehow. And there's not much precedence for a reasonable tradeoff, when you look at other game engines.

Maybe @coreh has some opinions here.