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