Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
4 changes: 2 additions & 2 deletions crates/bevy_core_pipeline/src/oit/oit_draw.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#import bevy_pbr::mesh_view_bindings::{view, oit_layers, oit_layer_ids, oit_settings}

#ifdef OIT_ENABLED
#ifdef OIT_VIEW
// Add the fragment to the oit buffer
fn oit_draw(position: vec4f, color: vec4f) {
// Don't add fully transparent fragments to the list
Expand Down Expand Up @@ -33,7 +33,7 @@ fn oit_draw(position: vec4f, color: vec4f) {
let depth_alpha = pack_24bit_depth_8bit_alpha(position.z, color.a);
oit_layers[layer_index] = vec2(rgb9e5_color, depth_alpha);
}
#endif // OIT_ENABLED
#endif // OIT_VIEW

fn pack_24bit_depth_8bit_alpha(depth: f32, alpha: f32) -> u32 {
let depth_bits = u32(saturate(depth) * f32(0xFFFFFFu) + 0.5);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_gizmos_render/src/pipeline_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ fn queue_line_gizmos_3d(
}

if oit {
view_key |= MeshPipelineKey::OIT_ENABLED;
view_key |= MeshPipelineKey::OIT_VIEW;
}

for (entity, main_entity, config) in &line_gizmos {
Expand Down
17 changes: 13 additions & 4 deletions crates/bevy_pbr/src/decal/forward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use bevy_math::{prelude::Rectangle, Quat, Vec2, Vec3};
use bevy_mesh::{Mesh, Mesh3d, MeshBuilder, MeshVertexBufferLayoutRef, Meshable};
use bevy_reflect::{Reflect, TypePath};
use bevy_render::{
alpha::AlphaMode,
render_asset::RenderAssets,
render_resource::{
AsBindGroup, AsBindGroupShaderType, CompareFunction, RenderPipelineDescriptor, ShaderType,
Expand Down Expand Up @@ -47,7 +46,7 @@ impl Plugin for ForwardDecalPlugin {
}
}

/// A decal that renders via a 1x1 transparent quad mesh, smoothly alpha-blending with the underlying
/// A decal that renders via a 1x1 quad mesh, smoothly alpha-blending with the underlying
/// geometry towards the edges.
///
/// Because forward decals are meshes, you can use arbitrary materials to control their appearance.
Expand Down Expand Up @@ -91,6 +90,7 @@ pub struct ForwardDecalMaterialExt {
/// blending with more distant surfaces.
///
/// Units are in meters.
/// This has no effect if alpha mode is `Opaque`.
pub depth_fade_factor: f32,
}

Expand All @@ -111,8 +111,17 @@ impl AsBindGroupShaderType<ForwardDecalMaterialExtUniform> for ForwardDecalMater
}

impl MaterialExtension for ForwardDecalMaterialExt {
fn alpha_mode() -> Option<AlphaMode> {
Some(AlphaMode::Blend)
fn enable_shadows() -> bool {
false
}

fn enable_prepass() -> bool {
false
}

// Forward decal is incompatible with OIT as it needs to be rendered even it's occluded by opaque objects, but OIT will do depth testing.
fn enable_oit() -> bool {
false
}

fn specialize(
Expand Down
13 changes: 12 additions & 1 deletion crates/bevy_pbr/src/extended_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ pub trait MaterialExtension: Asset + AsBindGroup + Clone + Sized {
true
}

/// If there is [`OrderIndependentTransparencySettings`](bevy_core_pipeline::oit::OrderIndependentTransparencySettings) in the camera,
/// controls whether to enable OIT or do regular alpha blending with sorting for the Material.
#[inline]
fn enable_oit() -> bool {
true
}

/// Returns this material's prepass vertex shader. If [`ShaderRef::Default`] is returned, the base material prepass vertex shader
/// will be used.
fn prepass_vertex_shader() -> ShaderRef {
Expand Down Expand Up @@ -352,7 +359,11 @@ impl<B: Material, E: MaterialExtension> Material for ExtendedMaterial<B, E> {
}

fn enable_shadows() -> bool {
E::enable_prepass()
E::enable_shadows()
}

fn enable_oit() -> bool {
E::enable_oit()
}

fn prepass_vertex_shader() -> ShaderRef {
Expand Down
16 changes: 16 additions & 0 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ pub trait Material: Asset + AsBindGroup + Clone + Sized {
false
}

/// If there is [`OrderIndependentTransparencySettings`](bevy_core_pipeline::oit::OrderIndependentTransparencySettings) in the camera,
/// controls whether to enable OIT or do regular alpha blending with sorting for the Material.
#[inline]
fn enable_oit() -> bool {
true
}

/// Controls if the prepass is enabled for the Material.
/// For more information about what a prepass is, see the [`bevy_core_pipeline::prepass`] docs.
#[inline]
Expand Down Expand Up @@ -1114,6 +1121,10 @@ pub fn specialize_material_meshes(
}
}

if material.properties.oit_enabled {
mesh_key |= MeshPipelineKey::OIT_ENABLED;
}

let erased_key = ErasedMaterialPipelineKey {
type_id: material_instance.asset_id.type_id(),
mesh_key,
Expand Down Expand Up @@ -1552,6 +1563,8 @@ pub struct MaterialProperties {
pub shadows_enabled: bool,
/// Whether prepass is enabled for this material
pub prepass_enabled: bool,
/// Whether order independent transparency is enabled for this material.
pub oit_enabled: bool,
}

impl MaterialProperties {
Expand Down Expand Up @@ -1658,6 +1671,7 @@ where
) -> Result<Self::ErasedAsset, PrepareAssetError<Self::SourceAsset>> {
let shadows_enabled = M::enable_shadows();
let prepass_enabled = M::enable_prepass();
let oit_enabled = M::enable_oit();

let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial>();
let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial>();
Expand Down Expand Up @@ -1827,6 +1841,7 @@ where
material_key,
shadows_enabled,
prepass_enabled,
oit_enabled,
}),
})
}
Expand Down Expand Up @@ -1871,6 +1886,7 @@ where
material_key,
shadows_enabled,
prepass_enabled,
oit_enabled,
}),
})
}
Expand Down
20 changes: 14 additions & 6 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ pub fn check_views_need_specialization(
}

if has_oit {
view_key |= MeshPipelineKey::OIT_ENABLED;
view_key |= MeshPipelineKey::OIT_VIEW;
}

if has_atmosphere {
Expand Down Expand Up @@ -2102,10 +2102,11 @@ bitflags::bitflags! {
const SCREEN_SPACE_REFLECTIONS = 1 << 17;
const HAS_PREVIOUS_SKIN = 1 << 18;
const HAS_PREVIOUS_MORPH = 1 << 19;
const OIT_ENABLED = 1 << 20;
const DISTANCE_FOG = 1 << 21;
const ATMOSPHERE = 1 << 22;
const INVERT_CULLING = 1 << 23;
const OIT_VIEW = 1 << 20;
const OIT_ENABLED = 1 << 21;
const DISTANCE_FOG = 1 << 22;
const ATMOSPHERE = 1 << 23;
const INVERT_CULLING = 1 << 24;
const LAST_FLAG = Self::INVERT_CULLING.bits();

// Bitfields
Expand Down Expand Up @@ -2377,12 +2378,19 @@ impl SpecializedMeshPipeline for MeshPipeline {
shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
}

if key.contains(MeshPipelineKey::OIT_VIEW) {
shader_defs.push("OIT_VIEW".into());
}

let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;

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_VIEW)
&& key.contains(MeshPipelineKey::OIT_ENABLED)
&& pass == MeshPipelineKey::BLEND_ALPHA
{
label = "oit_mesh_pipeline".into();
// TODO tail blending would need alpha blending
blend = None;
Expand Down
12 changes: 6 additions & 6 deletions crates/bevy_pbr/src/render/mesh_view_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ bitflags::bitflags! {
const NORMAL_PREPASS = 1 << 2;
const MOTION_VECTOR_PREPASS = 1 << 3;
const DEFERRED_PREPASS = 1 << 4;
const OIT_ENABLED = 1 << 5;
const OIT_VIEW = 1 << 5;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't really understand what OIT_VIEW means here and why it isn't just OIT_ENABLED?

Copy link
Contributor Author

@beicause beicause Dec 15, 2025

Choose a reason for hiding this comment

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

It is used for mesh view binding when OIT is enabled on the camera, otherwise the shader bind groups will not match.

These bindings seem to be distinguished by camera, but not by material.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd like to revert this change as I'm not convinced it's necessary or stable enough. IMO the rendering pipeline should just respect alpha_mode=blend for now, if OIT needs a separate alpha mode we can make changes in the future,.

const ATMOSPHERE = 1 << 6;
}
}
Expand Down Expand Up @@ -122,7 +122,7 @@ impl MeshPipelineViewLayoutKey {
} else {
Default::default()
},
if self.contains(Key::OIT_ENABLED) {
if self.contains(Key::OIT_VIEW) {
"_oit"
} else {
Default::default()
Expand Down Expand Up @@ -155,8 +155,8 @@ impl From<MeshPipelineKey> for MeshPipelineViewLayoutKey {
if value.contains(MeshPipelineKey::DEFERRED_PREPASS) {
result |= MeshPipelineViewLayoutKey::DEFERRED_PREPASS;
}
if value.contains(MeshPipelineKey::OIT_ENABLED) {
result |= MeshPipelineViewLayoutKey::OIT_ENABLED;
if value.contains(MeshPipelineKey::OIT_VIEW) {
result |= MeshPipelineViewLayoutKey::OIT_VIEW;
}
if value.contains(MeshPipelineKey::ATMOSPHERE) {
result |= MeshPipelineViewLayoutKey::ATMOSPHERE;
Expand Down Expand Up @@ -377,7 +377,7 @@ fn layout_entries(
));

// OIT
if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) {
if layout_key.contains(MeshPipelineViewLayoutKey::OIT_VIEW) {
// Check if we can use OIT. This is a hack to avoid errors on webgl --
// the OIT plugin will warn the user that OIT is not supported on their
// platform, so we don't need to do it here.
Expand Down Expand Up @@ -654,7 +654,7 @@ pub fn prepare_mesh_view_bind_groups(
let mut layout_key = MeshPipelineViewLayoutKey::from(*msaa)
| MeshPipelineViewLayoutKey::from(prepass_textures);
if has_oit {
layout_key |= MeshPipelineViewLayoutKey::OIT_ENABLED;
layout_key |= MeshPipelineViewLayoutKey::OIT_VIEW;
}
if has_atmosphere {
layout_key |= MeshPipelineViewLayoutKey::ATMOSPHERE;
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_pbr/src/render/mesh_view_bindings.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ const VISIBILITY_RANGE_UNIFORM_BUFFER_SIZE: u32 = 64u;
@group(0) @binding(24) var view_transmission_texture: texture_2d<f32>;
@group(0) @binding(25) var view_transmission_sampler: sampler;

#ifdef OIT_ENABLED
#ifdef OIT_VIEW
@group(0) @binding(26) var<storage, read_write> oit_layers: array<vec2<u32>>;
@group(0) @binding(27) var<storage, read_write> oit_layer_ids: array<atomic<i32>>;
@group(0) @binding(28) var<uniform> oit_settings: types::OrderIndependentTransparencySettings;
#endif // OIT_ENABLED
#endif // OIT_VIEW

#ifdef ATMOSPHERE
@group(0) @binding(29) var atmosphere_transmittance_texture: texture_2d<f32>;
Expand Down
46 changes: 44 additions & 2 deletions examples/3d/decal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,60 @@ fn setup(
) {
// Spawn the forward decal
commands.spawn((
Name::new("Decal"),
Name::new("Decal Red"),
ForwardDecal,
MeshMaterial3d(decal_standard_materials.add(ForwardDecalMaterial {
base: StandardMaterial {
base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
base_color: Color::srgba(1.0, 0.0, 0.0, 0.5),
// Alpha mode for decals, other modes are also supported
alpha_mode: AlphaMode::Blend,
depth_bias: 0.0,
..default()
},
extension: ForwardDecalMaterialExt {
depth_fade_factor: 1.0,
},
})),
Transform::from_scale(Vec3::splat(4.0)),
Transform::from_scale(Vec3::splat(4.0)).with_translation(Vec3::new(1.0, 0.0, -1.0)),
));

commands.spawn((
Name::new("Decal Green"),
ForwardDecal,
MeshMaterial3d(decal_standard_materials.add(ForwardDecalMaterial {
base: StandardMaterial {
base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
base_color: Color::srgba(0.0, 1.0, 0.0, 0.5),
// Alpha mode for decals, other modes are also supported
alpha_mode: AlphaMode::Blend,
depth_bias: 2.0,
..default()
},
extension: ForwardDecalMaterialExt {
depth_fade_factor: 1.0,
},
})),
Transform::from_scale(Vec3::splat(4.0)).with_translation(Vec3::new(-1.0, 0.0, -1.0)),
));

commands.spawn((
Name::new("Decal Blue"),
ForwardDecal,
MeshMaterial3d(decal_standard_materials.add(ForwardDecalMaterial {
base: StandardMaterial {
base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
base_color: Color::srgba(0.0, 0.0, 1.0, 0.5),
// Alpha mode for decals, other modes are also supported.
alpha_mode: AlphaMode::Blend,
depth_bias: 4.0,
..default()
},
extension: ForwardDecalMaterialExt {
depth_fade_factor: 1.0,
},
})),
Transform::from_scale(Vec3::splat(4.0)).with_translation(Vec3::new(0.0, 0.0, 1.0)),
));

commands.spawn((
Expand Down