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