Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion crates/bevy_core_pipeline/src/oit/oit_draw.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ fn oit_draw(position: vec4f, color: vec4f) {
#endif
// Don't add fully transparent fragments to the list
// because we don't want to have to sort them in the resolve pass
if color.a < oit_settings.alpha_threshold {
Copy link
Copy Markdown
Contributor

@beicause beicause Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do this somewhere before premultiply_alpha?
For Add and Premultiplied modes we should not cull alpha in here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we could consider removing this setting. After all users can do it themselves in custom materials.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be fixed now, i adjusted the check to work correctly for premultiplied alpha, should have the same results as before this change

Copy link
Copy Markdown
Contributor

@beicause beicause Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not correct to compare individual components. An alternative is to compare luminance.

I think there should be a luminance threshold setting for premultiplied or add mode.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would it not be correct to compare the individual components? it should be the same check as the one that was here before, just now it acts on premultiplied color

Copy link
Copy Markdown
Contributor

@beicause beicause Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering the color contribution of premultiplied alpha, it seems more reasonable to compare the emitted luminance rather than comparing each component individually

if color.r < oit_settings.alpha_threshold
&& color.g < oit_settings.alpha_threshold
&& color.b < oit_settings.alpha_threshold
&& color.a < oit_settings.alpha_threshold
{
return;
}
// get the index of the current fragment relative to the screen size
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/src/oit/resolve/oit_resolve.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ fn resolve(head: u32, opaque_depth: f32) -> vec4<f32> {
for (var i = 0u; i < sorted_frag_count; i += 1) {
let color = bevy_pbr::rgb9e5::rgb9e5_to_vec3_(fragment_list[i].color);
let alpha = packed_depth_alpha_get_alpha(fragment_list[i].depth_alpha);
var base_color = vec4(color.rgb * alpha, alpha);
var base_color = vec4(color.rgb, alpha);
final_color = blend(final_color, base_color);
if final_color.a == 1.0 {
break;
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3416,7 +3416,12 @@ impl SpecializedMeshPipeline for MeshPipeline {
let (label, blend, depth_write_enabled);
let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
let (mut is_opaque, mut alpha_to_coverage_enabled) = (false, false);
if key.contains(MeshPipelineKey::OIT_ENABLED) && pass == MeshPipelineKey::BLEND_ALPHA {
if key.contains(MeshPipelineKey::OIT_ENABLED)
&& matches!(
pass,
MeshPipelineKey::BLEND_ALPHA | MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA
)
{
label = "oit_mesh_pipeline".into();
// TODO tail blending would need alpha blending
blend = None;
Expand Down
12 changes: 11 additions & 1 deletion crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,17 @@ fn fragment(

#ifdef OIT_ENABLED
let alpha_mode = pbr_input.material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
if alpha_mode != pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND {
// The fragments will only be drawn during the oit resolve pass.
oit_draw(in.position, vec4(out.color.rgb * out.color.a, out.color.a));
discard;
}
if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_PREMULTIPLIED {
// The fragments will only be drawn during the oit resolve pass.
oit_draw(in.position, out.color);
discard;
}
if alpha_mode == pbr_types::STANDARD_MATERIAL_FLAGS_ALPHA_MODE_ADD {
// The fragments will only be drawn during the oit resolve pass.
oit_draw(in.position, out.color);
discard;
Expand Down
4 changes: 2 additions & 2 deletions examples/3d/order_independent_transparency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ fn spawn_spheres(
Mesh3d(sphere_handle.clone()),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: GREEN.with_alpha(alpha).into(),
alpha_mode: AlphaMode::Blend,
alpha_mode: AlphaMode::Premultiplied,
..default()
})),
Transform::from_translation(pos_b + offset),
Expand All @@ -172,7 +172,7 @@ fn spawn_spheres(
Mesh3d(sphere_handle.clone()),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: BLUE.with_alpha(alpha).into(),
alpha_mode: AlphaMode::Blend,
alpha_mode: AlphaMode::Add,
..default()
})),
Transform::from_translation(pos_c + offset),
Expand Down