Skip to content

Commit

Permalink
Add support for setting blending mode (#337)
Browse files Browse the repository at this point in the history
Add support for setting blending mode

Co-authored-by: Sludge <[email protected]>
  • Loading branch information
Soulghost and SludgePhD authored Jun 25, 2024
1 parent 573d6dd commit 8a20894
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 5 deletions.
29 changes: 28 additions & 1 deletion src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub enum SimulationCondition {
/// rendered during the [`Transparent2d`] render phase.
///
/// [`Transparent2d`]: bevy::core_pipeline::core_2d::Transparent2d
#[derive(Debug, Default, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Reflect, Serialize, Deserialize, Hash)]
#[non_exhaustive]
pub enum AlphaMode {
/// Render the effect with alpha blending.
Expand All @@ -127,6 +127,33 @@ pub enum AlphaMode {
#[default]
Blend,

/// Similar to [`AlphaMode::Blend`], however assumes RGB channel values are
/// [premultiplied](https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied).
///
/// For otherwise constant RGB values, behaves more like [`AlphaMode::Blend`] for
/// alpha values closer to 1.0, and more like [`AlphaMode::Add`] for
/// alpha values closer to 0.0.
///
/// Can be used to avoid “border” or “outline” artifacts that can occur
/// when using plain alpha-blended textures.
Premultiply,

/// Combines the color of the fragments with the colors behind them in an
/// additive process, (i.e. like light) producing lighter results.
///
/// Black produces no effect. Alpha values can be used to modulate the result.
///
/// Useful for effects like holograms, ghosts, lasers and other energy beams.
Add,

/// Combines the color of the fragments with the colors behind them in a
/// multiplicative process, (i.e. like pigments) producing darker results.
///
/// White produces no effect. Alpha values can be used to modulate the result.
///
/// Useful for effects like stained glass, window tint film and some colored liquids.
Multiply,

/// Render the effect with alpha masking.
///
/// With this mode, the final alpha value computed per particle fragment is
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,8 @@ pub struct CompiledParticleEffect {
z_layer_2d: FloatOrd,
/// Layout flags.
layout_flags: LayoutFlags,
/// Alpha mode.
alpha_mode: AlphaMode,
}

impl Default for CompiledParticleEffect {
Expand All @@ -1121,6 +1123,7 @@ impl Default for CompiledParticleEffect {
#[cfg(feature = "2d")]
z_layer_2d: FloatOrd(0.0),
layout_flags: LayoutFlags::NONE,
alpha_mode: default(),
}
}
}
Expand Down Expand Up @@ -1196,6 +1199,7 @@ impl CompiledParticleEffect {
};

self.layout_flags = shader_source.layout_flags;
self.alpha_mode = asset.alpha_mode;

let init_shader = shader_cache.get_or_insert(&asset.name, &shader_source.init, shaders);
let update_shaders: Vec<_> = shader_source
Expand Down
7 changes: 6 additions & 1 deletion src/render/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use super::{
effect_cache::{DispatchBufferIndices, EffectSlices},
EffectCacheId, GpuCompressedTransform, LayoutFlags,
};
use crate::{EffectAsset, EffectShader, ParticleLayout, PropertyLayout};
use crate::{AlphaMode, EffectAsset, EffectShader, ParticleLayout, PropertyLayout};

/// Data needed to render all batches pertaining to a specific effect.
#[derive(Debug, Component)]
Expand Down Expand Up @@ -39,6 +39,8 @@ pub(crate) struct EffectBatches {
pub particle_layout: ParticleLayout,
/// Flags describing the render layout.
pub layout_flags: LayoutFlags,
/// Alpha mode.
pub alpha_mode: AlphaMode,
/// Entities holding the source [`ParticleEffect`] instances which were
/// batched into this single batch. Used to determine visibility per view.
///
Expand Down Expand Up @@ -124,6 +126,7 @@ impl EffectBatches {
.collect(),
handle: input.handle,
layout_flags: input.layout_flags,
alpha_mode: input.alpha_mode,
image_handle: input.image_handle,
render_shaders: input.effect_shader.render,
init_pipeline_id,
Expand All @@ -149,6 +152,8 @@ pub(crate) struct BatchesInput {
pub effect_shader: EffectShader,
/// Various flags related to the effect.
pub layout_flags: LayoutFlags,
/// Alpha mode.
pub alpha_mode: AlphaMode,
/// Texture to modulate the particle color.
pub image_handle: Handle<Image>,
/// Number of particles to spawn for this effect.
Expand Down
45 changes: 42 additions & 3 deletions src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ use crate::{
effect_cache::DispatchBufferIndices,
},
spawn::EffectSpawner,
CompiledParticleEffect, EffectProperties, EffectShader, EffectSimulation, HanabiPlugin,
ParticleLayout, PropertyLayout, RemovedEffectsEvent, SimulationCondition, ToWgslString,
AlphaMode, CompiledParticleEffect, EffectProperties, EffectShader, EffectSimulation,
HanabiPlugin, ParticleLayout, PropertyLayout, RemovedEffectsEvent, SimulationCondition,
ToWgslString,
};

mod aligned_buffer_vec;
Expand Down Expand Up @@ -1000,6 +1001,8 @@ pub(crate) struct ParticleRenderPipelineKey {
/// Key: USE_ALPHA_MASK
/// The effect is rendered with alpha masking.
use_alpha_mask: bool,
/// The effect needs Alpha blend.
alpha_mode: AlphaMode,
/// Key: FLIPBOOK
/// The effect is rendered with flipbook texture animation based on the
/// sprite index of each particle.
Expand All @@ -1026,6 +1029,7 @@ impl Default for ParticleRenderPipelineKey {
has_image: false,
local_space_simulation: false,
use_alpha_mask: false,
alpha_mode: AlphaMode::Blend,
flipbook: false,
needs_uv: false,
#[cfg(all(feature = "2d", feature = "3d"))]
Expand Down Expand Up @@ -1213,6 +1217,32 @@ impl SpecializedRenderPipeline for ParticlesRenderPipeline {
TextureFormat::bevy_default()
};

let blend_state = match key.alpha_mode {
AlphaMode::Blend => BlendState::ALPHA_BLENDING,
AlphaMode::Premultiply => BlendState::PREMULTIPLIED_ALPHA_BLENDING,
AlphaMode::Add => BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::Zero,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
},
AlphaMode::Multiply => BlendState {
color: BlendComponent {
src_factor: BlendFactor::Dst,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent::OVER,
},
_ => BlendState::ALPHA_BLENDING,
};

RenderPipelineDescriptor {
vertex: VertexState {
shader: key.shader.clone(),
Expand All @@ -1226,7 +1256,7 @@ impl SpecializedRenderPipeline for ParticlesRenderPipeline {
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {
format,
blend: Some(BlendState::ALPHA_BLENDING),
blend: Some(blend_state),
write_mask: ColorWrites::ALL,
})],
}),
Expand Down Expand Up @@ -1290,6 +1320,8 @@ pub(crate) struct ExtractedEffect {
pub inverse_transform: Mat4,
/// Layout flags.
pub layout_flags: LayoutFlags,
/// Alpha mode.
pub alpha_mode: AlphaMode,
/// Texture to modulate the particle color.
pub image_handle: Handle<Image>,
/// Effect shader.
Expand Down Expand Up @@ -1518,6 +1550,8 @@ pub(crate) fn extract_effects(
layout_flags |= LayoutFlags::PARTICLE_TEXTURE;
}

let alpha_mode = effect.alpha_mode;

trace!(
"Extracted instance of effect '{}' on entity {:?}: image_handle={:?} has_image={} layout_flags={:?}",
asset.name,
Expand All @@ -1539,6 +1573,7 @@ pub(crate) fn extract_effects(
// TODO - more efficient/correct way than inverse()?
inverse_transform: transform.compute_matrix().inverse(),
layout_flags,
alpha_mode,
image_handle,
effect_shader,
#[cfg(feature = "2d")]
Expand Down Expand Up @@ -2073,6 +2108,7 @@ pub(crate) fn prepare_effects(
property_layout: extracted_effect.property_layout.clone(),
effect_shader: extracted_effect.effect_shader.clone(),
layout_flags: extracted_effect.layout_flags,
alpha_mode: extracted_effect.alpha_mode,
image_handle: extracted_effect.image_handle,
spawn_count: extracted_effect.spawn_count,
transform: extracted_effect.transform.into(),
Expand Down Expand Up @@ -2466,6 +2502,8 @@ fn emit_draw<T, F>(
let render_shader_source = &batches.render_shaders[draw_batch.group_index as usize];
trace!("Emit for group index #{}", draw_batch.group_index);

let alpha_mode = batches.alpha_mode;

#[cfg(feature = "trace")]
let _span_specialize = bevy::utils::tracing::info_span!("specialize").entered();
let render_pipeline_id = specialized_render_pipelines.specialize(
Expand All @@ -2477,6 +2515,7 @@ fn emit_draw<T, F>(
has_image,
local_space_simulation,
use_alpha_mask,
alpha_mode,
flipbook,
needs_uv,
#[cfg(all(feature = "2d", feature = "3d"))]
Expand Down

0 comments on commit 8a20894

Please sign in to comment.