From 197392a2cd2fbec4aab2dd521e22bbbe318a999c Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 28 Sep 2022 05:54:11 +0000 Subject: [PATCH] use alpha mask even when unlit (#6047) # Objective - Alpha mask was previously ignored when using an unlit material. - Fixes https://github.com/bevyengine/bevy/issues/4479 ## Solution - Extract the alpha discard to a separate function and use it when unlit is true ## Notes I tried calling `alpha_discard()` before the `if` in pbr.wgsl, but I had errors related to having a `discard` at the beginning before doing the texture sampling. I'm not sure if there's a way to fix that instead of having the function being called in 2 places. --- crates/bevy_pbr/src/render/pbr.wgsl | 2 ++ crates/bevy_pbr/src/render/pbr_functions.wgsl | 33 +++++++++++-------- examples/3d/transparency_3d.rs | 15 +++++++++ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index b70b5b333943c..cf71d1829a7a6 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -89,6 +89,8 @@ fn fragment(in: FragmentInput) -> @location(0) vec4 { pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); output_color = tone_mapping(pbr(pbr_input)); + } else { + output_color = alpha_discard(material, output_color); } return output_color; diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index a77c065fca221..08c4235ab09d3 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -1,5 +1,23 @@ #define_import_path bevy_pbr::pbr_functions +fn alpha_discard(material: StandardMaterial, output_color: vec4) -> vec4{ + var color = output_color; + if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE) != 0u) { + // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0 + color.a = 1.0; + } else if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u) { + if (color.a >= material.alpha_cutoff) { + // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque + color.a = 1.0; + } else { + // NOTE: output_color.a < in.material.alpha_cutoff should not is not rendered + // NOTE: This and any other discards mean that early-z testing cannot be done! + discard; + } + } + return color; +} + // NOTE: This ensures that the world_normal is normalized and if // vertex tangents and normal maps then normal mapping may be applied. fn prepare_normal( @@ -142,19 +160,7 @@ fn pbr( let occlusion = in.occlusion; - if ((in.material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE) != 0u) { - // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0 - output_color.a = 1.0; - } else if ((in.material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u) { - if (output_color.a >= in.material.alpha_cutoff) { - // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque - output_color.a = 1.0; - } else { - // NOTE: output_color.a < in.material.alpha_cutoff should not is not rendered - // NOTE: This and any other discards mean that early-z testing cannot be done! - discard; - } - } + output_color = alpha_discard(in.material, output_color); // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" let NdotV = max(dot(in.N, in.V), 0.0001); @@ -247,3 +253,4 @@ fn tone_mapping(in: vec4) -> vec4 { // Not needed with sRGB buffer // output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2)); } + diff --git a/examples/3d/transparency_3d.rs b/examples/3d/transparency_3d.rs index e04304c8f1cce..4f02259276517 100644 --- a/examples/3d/transparency_3d.rs +++ b/examples/3d/transparency_3d.rs @@ -44,6 +44,21 @@ fn setup( transform: Transform::from_xyz(1.0, 0.5, -1.5), ..default() }); + // transparent unlit sphere, uses `alpha_mode: Mask(f32)` + commands.spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Icosphere { + radius: 0.5, + subdivisions: 3, + })), + material: materials.add(StandardMaterial { + base_color: Color::rgba(0.2, 0.7, 0.1, 0.0), + alpha_mode: AlphaMode::Mask(0.5), + unlit: true, + ..default() + }), + transform: Transform::from_xyz(-1.0, 0.5, -1.5), + ..default() + }); // transparent cube, uses `alpha_mode: Blend` commands.spawn(PbrBundle { mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),