1use crate::{
2 graph::NodePbr, MeshPipeline, MeshViewBindGroup, RenderViewLightProbes,
3 ScreenSpaceAmbientOcclusion, ScreenSpaceReflectionsUniform, ViewEnvironmentMapUniformOffset,
4 ViewLightProbesUniformOffset, ViewScreenSpaceReflectionsUniformOffset,
5 TONEMAPPING_LUT_SAMPLER_BINDING_INDEX, TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
6};
7use crate::{
8 DistanceFog, ExtractedAtmosphere, MeshPipelineKey, ViewFogUniformOffset,
9 ViewLightsUniformOffset,
10};
11use bevy_app::prelude::*;
12use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
13use bevy_core_pipeline::{
14 core_3d::graph::{Core3d, Node3d},
15 deferred::{
16 copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
17 },
18 prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
19 tonemapping::{DebandDither, Tonemapping},
20};
21use bevy_ecs::{prelude::*, query::QueryItem};
22use bevy_image::BevyDefault as _;
23use bevy_light::{EnvironmentMapLight, IrradianceVolume, ShadowFilteringMethod};
24use bevy_render::RenderStartup;
25use bevy_render::{
26 diagnostic::RecordDiagnostics,
27 extract_component::{
28 ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
29 },
30 render_graph::{NodeRunError, RenderGraphContext, RenderGraphExt, ViewNode, ViewNodeRunner},
31 render_resource::{binding_types::uniform_buffer, *},
32 renderer::RenderContext,
33 view::{ExtractedView, ViewTarget, ViewUniformOffset},
34 Render, RenderApp, RenderSystems,
35};
36use bevy_shader::{Shader, ShaderDefVal};
37use bevy_utils::default;
38
39pub struct DeferredPbrLightingPlugin;
40
41pub const DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID: u8 = 1;
42
43#[derive(Component, Clone, Copy, ExtractComponent, ShaderType)]
47pub struct PbrDeferredLightingDepthId {
48 depth_id: u32,
49
50 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
51 _webgl2_padding_0: f32,
52 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
53 _webgl2_padding_1: f32,
54 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
55 _webgl2_padding_2: f32,
56}
57
58impl PbrDeferredLightingDepthId {
59 pub fn new(value: u8) -> PbrDeferredLightingDepthId {
60 PbrDeferredLightingDepthId {
61 depth_id: value as u32,
62
63 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
64 _webgl2_padding_0: 0.0,
65 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
66 _webgl2_padding_1: 0.0,
67 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
68 _webgl2_padding_2: 0.0,
69 }
70 }
71
72 pub fn set(&mut self, value: u8) {
73 self.depth_id = value as u32;
74 }
75
76 pub fn get(&self) -> u8 {
77 self.depth_id as u8
78 }
79}
80
81impl Default for PbrDeferredLightingDepthId {
82 fn default() -> Self {
83 PbrDeferredLightingDepthId {
84 depth_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID as u32,
85
86 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
87 _webgl2_padding_0: 0.0,
88 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
89 _webgl2_padding_1: 0.0,
90 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
91 _webgl2_padding_2: 0.0,
92 }
93 }
94}
95
96impl Plugin for DeferredPbrLightingPlugin {
97 fn build(&self, app: &mut App) {
98 app.add_plugins((
99 ExtractComponentPlugin::<PbrDeferredLightingDepthId>::default(),
100 UniformComponentPlugin::<PbrDeferredLightingDepthId>::default(),
101 ))
102 .add_systems(PostUpdate, insert_deferred_lighting_pass_id_component);
103
104 embedded_asset!(app, "deferred_lighting.wgsl");
105
106 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
107 return;
108 };
109
110 render_app
111 .init_resource::<SpecializedRenderPipelines<DeferredLightingLayout>>()
112 .add_systems(RenderStartup, init_deferred_lighting_layout)
113 .add_systems(
114 Render,
115 (prepare_deferred_lighting_pipelines.in_set(RenderSystems::Prepare),),
116 )
117 .add_render_graph_node::<ViewNodeRunner<DeferredOpaquePass3dPbrLightingNode>>(
118 Core3d,
119 NodePbr::DeferredLightingPass,
120 )
121 .add_render_graph_edges(
122 Core3d,
123 (
124 Node3d::StartMainPass,
125 NodePbr::DeferredLightingPass,
126 Node3d::MainOpaquePass,
127 ),
128 );
129 }
130}
131
132#[derive(Default)]
133pub struct DeferredOpaquePass3dPbrLightingNode;
134
135impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
136 type ViewQuery = (
137 &'static ViewUniformOffset,
138 &'static ViewLightsUniformOffset,
139 &'static ViewFogUniformOffset,
140 &'static ViewLightProbesUniformOffset,
141 &'static ViewScreenSpaceReflectionsUniformOffset,
142 &'static ViewEnvironmentMapUniformOffset,
143 &'static MeshViewBindGroup,
144 &'static ViewTarget,
145 &'static DeferredLightingIdDepthTexture,
146 &'static DeferredLightingPipeline,
147 );
148
149 fn run(
150 &self,
151 _graph_context: &mut RenderGraphContext,
152 render_context: &mut RenderContext,
153 (
154 view_uniform_offset,
155 view_lights_offset,
156 view_fog_offset,
157 view_light_probes_offset,
158 view_ssr_offset,
159 view_environment_map_offset,
160 mesh_view_bind_group,
161 target,
162 deferred_lighting_id_depth_texture,
163 deferred_lighting_pipeline,
164 ): QueryItem<Self::ViewQuery>,
165 world: &World,
166 ) -> Result<(), NodeRunError> {
167 let pipeline_cache = world.resource::<PipelineCache>();
168 let deferred_lighting_layout = world.resource::<DeferredLightingLayout>();
169
170 let Some(pipeline) =
171 pipeline_cache.get_render_pipeline(deferred_lighting_pipeline.pipeline_id)
172 else {
173 return Ok(());
174 };
175
176 let deferred_lighting_pass_id =
177 world.resource::<ComponentUniforms<PbrDeferredLightingDepthId>>();
178 let Some(deferred_lighting_pass_id_binding) =
179 deferred_lighting_pass_id.uniforms().binding()
180 else {
181 return Ok(());
182 };
183
184 let diagnostics = render_context.diagnostic_recorder();
185
186 let bind_group_2 = render_context.render_device().create_bind_group(
187 "deferred_lighting_layout_group_2",
188 &pipeline_cache.get_bind_group_layout(&deferred_lighting_layout.bind_group_layout_2),
189 &BindGroupEntries::single(deferred_lighting_pass_id_binding),
190 );
191
192 let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
193 label: Some("deferred_lighting"),
194 color_attachments: &[Some(target.get_color_attachment())],
195 depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
196 view: &deferred_lighting_id_depth_texture.texture.default_view,
197 depth_ops: Some(Operations {
198 load: LoadOp::Load,
199 store: StoreOp::Discard,
200 }),
201 stencil_ops: None,
202 }),
203 timestamp_writes: None,
204 occlusion_query_set: None,
205 });
206 let pass_span = diagnostics.pass_span(&mut render_pass, "deferred_lighting");
207
208 render_pass.set_render_pipeline(pipeline);
209 render_pass.set_bind_group(
210 0,
211 &mesh_view_bind_group.main,
212 &[
213 view_uniform_offset.offset,
214 view_lights_offset.offset,
215 view_fog_offset.offset,
216 **view_light_probes_offset,
217 **view_ssr_offset,
218 **view_environment_map_offset,
219 ],
220 );
221 render_pass.set_bind_group(1, &mesh_view_bind_group.binding_array, &[]);
222 render_pass.set_bind_group(2, &bind_group_2, &[]);
223 render_pass.draw(0..3, 0..1);
224
225 pass_span.end(&mut render_pass);
226
227 Ok(())
228 }
229}
230
231#[derive(Resource)]
232pub struct DeferredLightingLayout {
233 mesh_pipeline: MeshPipeline,
234 bind_group_layout_2: BindGroupLayoutDescriptor,
235 deferred_lighting_shader: Handle<Shader>,
236}
237
238#[derive(Component)]
239pub struct DeferredLightingPipeline {
240 pub pipeline_id: CachedRenderPipelineId,
241}
242
243impl SpecializedRenderPipeline for DeferredLightingLayout {
244 type Key = MeshPipelineKey;
245
246 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
247 let mut shader_defs = Vec::new();
248
249 shader_defs.push("DEFERRED_LIGHTING_PIPELINE".into());
251
252 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
253 shader_defs.push("WEBGL2".into());
254
255 if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
256 shader_defs.push("TONEMAP_IN_SHADER".into());
257 shader_defs.push(ShaderDefVal::UInt(
258 "TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
259 TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
260 ));
261 shader_defs.push(ShaderDefVal::UInt(
262 "TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
263 TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
264 ));
265
266 let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
267
268 if method == MeshPipelineKey::TONEMAP_METHOD_NONE {
269 shader_defs.push("TONEMAP_METHOD_NONE".into());
270 } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD {
271 shader_defs.push("TONEMAP_METHOD_REINHARD".into());
272 } else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
273 shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
274 } else if method == MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED {
275 shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
276 } else if method == MeshPipelineKey::TONEMAP_METHOD_AGX {
277 shader_defs.push("TONEMAP_METHOD_AGX".into());
278 } else if method == MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM {
279 shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
280 } else if method == MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
281 shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
282 } else if method == MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
283 shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
284 }
285
286 if key.contains(MeshPipelineKey::DEBAND_DITHER) {
288 shader_defs.push("DEBAND_DITHER".into());
289 }
290 }
291
292 if key.contains(MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
293 shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
294 }
295
296 if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
297 shader_defs.push("ENVIRONMENT_MAP".into());
298 }
299
300 if key.contains(MeshPipelineKey::IRRADIANCE_VOLUME) {
301 shader_defs.push("IRRADIANCE_VOLUME".into());
302 }
303
304 if key.contains(MeshPipelineKey::NORMAL_PREPASS) {
305 shader_defs.push("NORMAL_PREPASS".into());
306 }
307
308 if key.contains(MeshPipelineKey::DEPTH_PREPASS) {
309 shader_defs.push("DEPTH_PREPASS".into());
310 }
311
312 if key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
313 shader_defs.push("MOTION_VECTOR_PREPASS".into());
314 }
315
316 if key.contains(MeshPipelineKey::SCREEN_SPACE_REFLECTIONS) {
317 shader_defs.push("SCREEN_SPACE_REFLECTIONS".into());
318 }
319
320 if key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
321 shader_defs.push("HAS_PREVIOUS_SKIN".into());
322 }
323
324 if key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
325 shader_defs.push("HAS_PREVIOUS_MORPH".into());
326 }
327
328 if key.contains(MeshPipelineKey::DISTANCE_FOG) {
329 shader_defs.push("DISTANCE_FOG".into());
330 }
331 if key.contains(MeshPipelineKey::ATMOSPHERE) {
332 shader_defs.push("ATMOSPHERE".into());
333 }
334
335 shader_defs.push("DEFERRED_PREPASS".into());
337
338 let shadow_filter_method =
339 key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
340 if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
341 shader_defs.push("SHADOW_FILTER_METHOD_HARDWARE_2X2".into());
342 } else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN {
343 shader_defs.push("SHADOW_FILTER_METHOD_GAUSSIAN".into());
344 } else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL {
345 shader_defs.push("SHADOW_FILTER_METHOD_TEMPORAL".into());
346 }
347 if self.mesh_pipeline.binding_arrays_are_usable {
348 shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
349 shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
350 }
351
352 #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
353 shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());
354
355 let layout = self.mesh_pipeline.get_view_layout(key.into());
356 RenderPipelineDescriptor {
357 label: Some("deferred_lighting_pipeline".into()),
358 layout: vec![
359 layout.main_layout.clone(),
360 layout.binding_array_layout.clone(),
361 self.bind_group_layout_2.clone(),
362 ],
363 vertex: VertexState {
364 shader: self.deferred_lighting_shader.clone(),
365 shader_defs: shader_defs.clone(),
366 ..default()
367 },
368 fragment: Some(FragmentState {
369 shader: self.deferred_lighting_shader.clone(),
370 shader_defs,
371 targets: vec![Some(ColorTargetState {
372 format: if key.contains(MeshPipelineKey::HDR) {
373 ViewTarget::TEXTURE_FORMAT_HDR
374 } else {
375 TextureFormat::bevy_default()
376 },
377 blend: None,
378 write_mask: ColorWrites::ALL,
379 })],
380 ..default()
381 }),
382 depth_stencil: Some(DepthStencilState {
383 format: DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
384 depth_write_enabled: false,
385 depth_compare: CompareFunction::Equal,
386 stencil: StencilState {
387 front: StencilFaceState::IGNORE,
388 back: StencilFaceState::IGNORE,
389 read_mask: 0,
390 write_mask: 0,
391 },
392 bias: DepthBiasState {
393 constant: 0,
394 slope_scale: 0.0,
395 clamp: 0.0,
396 },
397 }),
398 ..default()
399 }
400 }
401}
402
403pub fn init_deferred_lighting_layout(
404 mut commands: Commands,
405 mesh_pipeline: Res<MeshPipeline>,
406 asset_server: Res<AssetServer>,
407) {
408 let layout = BindGroupLayoutDescriptor::new(
409 "deferred_lighting_layout",
410 &BindGroupLayoutEntries::single(
411 ShaderStages::VERTEX_FRAGMENT,
412 uniform_buffer::<PbrDeferredLightingDepthId>(false),
413 ),
414 );
415 commands.insert_resource(DeferredLightingLayout {
416 mesh_pipeline: mesh_pipeline.clone(),
417 bind_group_layout_2: layout,
418 deferred_lighting_shader: load_embedded_asset!(
419 asset_server.as_ref(),
420 "deferred_lighting.wgsl"
421 ),
422 });
423}
424
425pub fn insert_deferred_lighting_pass_id_component(
426 mut commands: Commands,
427 views: Query<Entity, (With<DeferredPrepass>, Without<PbrDeferredLightingDepthId>)>,
428) {
429 for entity in views.iter() {
430 commands
431 .entity(entity)
432 .insert(PbrDeferredLightingDepthId::default());
433 }
434}
435
436pub fn prepare_deferred_lighting_pipelines(
437 mut commands: Commands,
438 pipeline_cache: Res<PipelineCache>,
439 mut pipelines: ResMut<SpecializedRenderPipelines<DeferredLightingLayout>>,
440 deferred_lighting_layout: Res<DeferredLightingLayout>,
441 views: Query<(
442 Entity,
443 &ExtractedView,
444 Option<&Tonemapping>,
445 Option<&DebandDither>,
446 Option<&ShadowFilteringMethod>,
447 (
448 Has<ScreenSpaceAmbientOcclusion>,
449 Has<ScreenSpaceReflectionsUniform>,
450 Has<DistanceFog>,
451 ),
452 (
453 Has<NormalPrepass>,
454 Has<DepthPrepass>,
455 Has<MotionVectorPrepass>,
456 Has<DeferredPrepass>,
457 ),
458 Has<RenderViewLightProbes<EnvironmentMapLight>>,
459 Has<RenderViewLightProbes<IrradianceVolume>>,
460 Has<SkipDeferredLighting>,
461 Has<ExtractedAtmosphere>,
462 )>,
463) {
464 for (
465 entity,
466 view,
467 tonemapping,
468 dither,
469 shadow_filter_method,
470 (ssao, ssr, distance_fog),
471 (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
472 has_environment_maps,
473 has_irradiance_volumes,
474 skip_deferred_lighting,
475 has_atmosphere,
476 ) in &views
477 {
478 if !deferred_prepass || skip_deferred_lighting {
482 commands.entity(entity).remove::<DeferredLightingPipeline>();
483 continue;
484 }
485
486 let mut view_key = MeshPipelineKey::from_hdr(view.hdr);
487
488 if normal_prepass {
489 view_key |= MeshPipelineKey::NORMAL_PREPASS;
490 }
491
492 if depth_prepass {
493 view_key |= MeshPipelineKey::DEPTH_PREPASS;
494 }
495
496 if motion_vector_prepass {
497 view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
498 }
499
500 if has_atmosphere {
501 view_key |= MeshPipelineKey::ATMOSPHERE;
502 }
503
504 if view.invert_culling {
505 view_key |= MeshPipelineKey::INVERT_CULLING;
506 }
507
508 view_key |= MeshPipelineKey::DEFERRED_PREPASS;
510
511 if !view.hdr {
512 if let Some(tonemapping) = tonemapping {
513 view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
514 view_key |= match tonemapping {
515 Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
516 Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
517 Tonemapping::ReinhardLuminance => {
518 MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE
519 }
520 Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
521 Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
522 Tonemapping::SomewhatBoringDisplayTransform => {
523 MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
524 }
525 Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
526 Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
527 };
528 }
529 if let Some(DebandDither::Enabled) = dither {
530 view_key |= MeshPipelineKey::DEBAND_DITHER;
531 }
532 }
533
534 if ssao {
535 view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
536 }
537 if ssr {
538 view_key |= MeshPipelineKey::SCREEN_SPACE_REFLECTIONS;
539 }
540 if distance_fog {
541 view_key |= MeshPipelineKey::DISTANCE_FOG;
542 }
543
544 if has_environment_maps {
548 view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
549 }
550
551 if has_irradiance_volumes {
552 view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
553 }
554
555 match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
556 ShadowFilteringMethod::Hardware2x2 => {
557 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
558 }
559 ShadowFilteringMethod::Gaussian => {
560 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
561 }
562 ShadowFilteringMethod::Temporal => {
563 view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
564 }
565 }
566
567 let pipeline_id =
568 pipelines.specialize(&pipeline_cache, &deferred_lighting_layout, view_key);
569
570 commands
571 .entity(entity)
572 .insert(DeferredLightingPipeline { pipeline_id });
573 }
574}
575
576#[derive(Component, Clone, Copy, Default)]
585pub struct SkipDeferredLighting;