From 3a478ad5c1b44369ac2d955a7a6b523df08faf3f Mon Sep 17 00:00:00 2001 From: Sou1gh0st Date: Sat, 19 Oct 2024 07:18:11 +0800 Subject: [PATCH] Fix lightmaps break when deferred rendering is enabled (#14599) # Objective - Fixes https://github.com/bevyengine/bevy/issues/13552 ## Solution - Thanks for the guidance from @DGriffin91, the current solution is to transmit the light_map through the emissive channel to avoid increasing the bandwidth of deferred shading. - Store lightmap sample result into G-Buffer and pass them into the `Deferred Lighting Pipeline`, therefore we can get the correct indirect lighting via the `apply_pbr_lighting` function. - The original G-Buffer lacks storage for lightmap data, therefore a new buffer is added. We can only use Rgba16Uint here due to the 32-byte limit on the render targets. ## Testing - Need to test all the examples that contains a prepass, with both the forward and deferred rendering mode. - I have tested the ones below. - `lightmaps` (adjust the code based on the issue and check the rendering result) - `transmission` (it contains a prepass) - `ssr` (it also uses the G-Bufffer) - `meshlet` (forward and deferred) - `pbr` ## Showcase By updating the `lightmaps` example to use deferred rendering, this pull request enables correct rendering result of the Cornell Box. ``` diff --git a/examples/3d/lightmaps.rs b/examples/3d/lightmaps.rs index 564a3162b..11a748fba 100644 --- a/examples/3d/lightmaps.rs +++ b/examples/3d/lightmaps.rs @@ -1,12 +1,14 @@ //! Rendering a scene with baked lightmaps. -use bevy::pbr::Lightmap; +use bevy::core_pipeline::prepass::DeferredPrepass; +use bevy::pbr::{DefaultOpaqueRendererMethod, Lightmap}; use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) .insert_resource(AmbientLight::NONE) + .insert_resource(DefaultOpaqueRendererMethod::deferred()) .add_systems(Startup, setup) .add_systems(Update, add_lightmaps_to_meshes) .run(); @@ -19,10 +21,12 @@ fn setup(mut commands: Commands, asset_server: Res) { ..default() }); - commands.spawn(Camera3dBundle { - transform: Transform::from_xyz(-278.0, 273.0, 800.0), - ..default() - }); + commands + .spawn(Camera3dBundle { + transform: Transform::from_xyz(-278.0, 273.0, 800.0), + ..default() + }) + .insert(DeferredPrepass); } fn add_lightmaps_to_meshes( ``` image ## Emissive Issue **The emissive light object appears incorrectly rendered because the alpha channel of emission is set to 1 in deferred rendering and 0 in forward rendering, leading to different emissive light result. Could this be a bug?** ```wgsl // pbr_deferred_functions.wgsl - pbr_input_from_deferred_gbuffer let emissive = rgb9e5::rgb9e5_to_vec3_(gbuffer.g); if ((pbr.material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) != 0u) { pbr.material.base_color = vec4(emissive, 1.0); pbr.material.emissive = vec4(vec3(0.0), 1.0); } else { pbr.material.base_color = vec4(pow(base_rough.rgb, vec3(2.2)), 1.0); pbr.material.emissive = vec4(emissive, 1.0); } // pbr_functions.wgsl - apply_pbr_lighting emissive_light = emissive_light * mix(1.0, view_bindings::view.exposure, emissive.a); ``` --------- Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com> --- .../src/deferred/pbr_deferred_functions.wgsl | 16 ++++++++++++++++ crates/bevy_pbr/src/prepass/mod.rs | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl b/crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl index b892181377..e96de6bded 100644 --- a/crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl +++ b/crates/bevy_pbr/src/deferred/pbr_deferred_functions.wgsl @@ -53,6 +53,22 @@ fn deferred_gbuffer_from_pbr_input(in: PbrInput) -> vec4 { } else { base_color_srgb = pow(in.material.base_color.rgb, vec3(1.0 / 2.2)); } + + // Utilize the emissive channel to transmit the lightmap data. To ensure + // it matches the output in forward shading, pre-multiply it with the + // calculated diffuse color. + let base_color = in.material.base_color.rgb; + let metallic = in.material.metallic; + let specular_transmission = in.material.specular_transmission; + let diffuse_transmission = in.material.diffuse_transmission; + let diffuse_color = pbr_functions::calculate_diffuse_color( + base_color, + metallic, + specular_transmission, + diffuse_transmission + ); + emissive += in.lightmap_light * diffuse_color * view.exposure; + let deferred = vec4( deferred_types::pack_unorm4x8_(vec4(base_color_srgb, in.material.perceptual_roughness)), rgb9e5::vec3_to_rgb9e5_(emissive), diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index bcaef63068..39de9d029a 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -429,6 +429,10 @@ where shader_defs.push("DEFERRED_PREPASS".into()); } + if key.mesh_key.contains(MeshPipelineKey::LIGHTMAPPED) { + shader_defs.push("LIGHTMAP".into()); + } + if layout.0.contains(Mesh::ATTRIBUTE_COLOR) { shader_defs.push("VERTEX_COLORS".into()); vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7));