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