Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/deferred/mod.rs
9396 views
1
use crate::{
2
DistanceFog, ExtractedAtmosphere, MeshPipelineKey, ViewFogUniformOffset,
3
ViewLightsUniformOffset,
4
};
5
use crate::{
6
MeshPipeline, MeshViewBindGroup, RenderViewLightProbes, ScreenSpaceAmbientOcclusion,
7
ScreenSpaceReflectionsUniform, ViewContactShadowsUniformOffset,
8
ViewEnvironmentMapUniformOffset, ViewLightProbesUniformOffset,
9
ViewScreenSpaceReflectionsUniformOffset, TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
10
TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
11
};
12
use bevy_app::prelude::*;
13
use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
14
use bevy_core_pipeline::{
15
core_3d::main_opaque_pass_3d,
16
deferred::{
17
copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
18
},
19
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
20
schedule::{Core3d, Core3dSystems},
21
tonemapping::{DebandDither, Tonemapping},
22
};
23
use bevy_ecs::prelude::*;
24
use bevy_image::BevyDefault as _;
25
use bevy_light::{EnvironmentMapLight, IrradianceVolume, ShadowFilteringMethod};
26
use bevy_render::RenderStartup;
27
use bevy_render::{
28
extract_component::{
29
ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
30
},
31
render_resource::{binding_types::uniform_buffer, *},
32
renderer::{RenderContext, ViewQuery},
33
view::{ExtractedView, ViewTarget, ViewUniformOffset},
34
Render, RenderApp, RenderSystems,
35
};
36
use bevy_shader::{Shader, ShaderDefVal};
37
use bevy_utils::default;
38
39
pub struct DeferredPbrLightingPlugin;
40
41
pub const DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID: u8 = 1;
42
43
/// Component with a `depth_id` for specifying which corresponding materials should be rendered by this specific PBR deferred lighting pass.
44
///
45
/// Will be automatically added to entities with the [`DeferredPrepass`] component that don't already have a [`PbrDeferredLightingDepthId`].
46
#[derive(Component, Clone, Copy, ExtractComponent, ShaderType)]
47
pub 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
58
impl 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
81
impl 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
96
impl 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_systems(
118
Core3d,
119
deferred_lighting
120
.before(main_opaque_pass_3d)
121
.in_set(Core3dSystems::MainPass),
122
);
123
}
124
}
125
126
pub fn deferred_lighting(
127
view: ViewQuery<(
128
&ViewUniformOffset,
129
&ViewLightsUniformOffset,
130
&ViewFogUniformOffset,
131
&ViewLightProbesUniformOffset,
132
&ViewScreenSpaceReflectionsUniformOffset,
133
&ViewContactShadowsUniformOffset,
134
&ViewEnvironmentMapUniformOffset,
135
&MeshViewBindGroup,
136
&ViewTarget,
137
&DeferredLightingIdDepthTexture,
138
&DeferredLightingPipeline,
139
)>,
140
pipeline_cache: Res<PipelineCache>,
141
deferred_lighting_layout: Res<DeferredLightingLayout>,
142
deferred_lighting_pass_id: Res<ComponentUniforms<PbrDeferredLightingDepthId>>,
143
mut ctx: RenderContext,
144
) {
145
let (
146
view_uniform_offset,
147
view_lights_offset,
148
view_fog_offset,
149
view_light_probes_offset,
150
view_ssr_offset,
151
view_contact_shadows_offset,
152
view_environment_map_offset,
153
mesh_view_bind_group,
154
target,
155
deferred_lighting_id_depth_texture,
156
deferred_lighting_pipeline,
157
) = view.into_inner();
158
159
let Some(pipeline) = pipeline_cache.get_render_pipeline(deferred_lighting_pipeline.pipeline_id)
160
else {
161
return;
162
};
163
164
let Some(deferred_lighting_pass_id_binding) = deferred_lighting_pass_id.uniforms().binding()
165
else {
166
return;
167
};
168
169
let bind_group_2 = ctx.render_device().create_bind_group(
170
"deferred_lighting_layout_group_2",
171
&pipeline_cache.get_bind_group_layout(&deferred_lighting_layout.bind_group_layout_2),
172
&BindGroupEntries::single(deferred_lighting_pass_id_binding),
173
);
174
175
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
176
label: Some("deferred_lighting"),
177
color_attachments: &[Some(target.get_color_attachment())],
178
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
179
view: &deferred_lighting_id_depth_texture.texture.default_view,
180
depth_ops: Some(Operations {
181
load: LoadOp::Load,
182
store: StoreOp::Discard,
183
}),
184
stencil_ops: None,
185
}),
186
timestamp_writes: None,
187
occlusion_query_set: None,
188
multiview_mask: None,
189
});
190
191
render_pass.set_render_pipeline(pipeline);
192
render_pass.set_bind_group(
193
0,
194
&mesh_view_bind_group.main,
195
&[
196
view_uniform_offset.offset,
197
view_lights_offset.offset,
198
view_fog_offset.offset,
199
**view_light_probes_offset,
200
**view_ssr_offset,
201
**view_contact_shadows_offset,
202
**view_environment_map_offset,
203
],
204
);
205
render_pass.set_bind_group(1, &mesh_view_bind_group.binding_array, &[]);
206
render_pass.set_bind_group(2, &bind_group_2, &[]);
207
render_pass.draw(0..3, 0..1);
208
}
209
210
#[derive(Resource)]
211
pub struct DeferredLightingLayout {
212
mesh_pipeline: MeshPipeline,
213
bind_group_layout_2: BindGroupLayoutDescriptor,
214
deferred_lighting_shader: Handle<Shader>,
215
}
216
217
#[derive(Component)]
218
pub struct DeferredLightingPipeline {
219
pub pipeline_id: CachedRenderPipelineId,
220
}
221
222
impl SpecializedRenderPipeline for DeferredLightingLayout {
223
type Key = MeshPipelineKey;
224
225
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
226
let mut shader_defs = Vec::new();
227
228
// Let the shader code know that it's running in a deferred pipeline.
229
shader_defs.push("DEFERRED_LIGHTING_PIPELINE".into());
230
231
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
232
shader_defs.push("WEBGL2".into());
233
234
if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
235
shader_defs.push("TONEMAP_IN_SHADER".into());
236
shader_defs.push(ShaderDefVal::UInt(
237
"TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
238
TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
239
));
240
shader_defs.push(ShaderDefVal::UInt(
241
"TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
242
TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
243
));
244
245
let method = key.intersection(MeshPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
246
247
if method == MeshPipelineKey::TONEMAP_METHOD_NONE {
248
shader_defs.push("TONEMAP_METHOD_NONE".into());
249
} else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD {
250
shader_defs.push("TONEMAP_METHOD_REINHARD".into());
251
} else if method == MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE {
252
shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
253
} else if method == MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED {
254
shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
255
} else if method == MeshPipelineKey::TONEMAP_METHOD_AGX {
256
shader_defs.push("TONEMAP_METHOD_AGX".into());
257
} else if method == MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM {
258
shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
259
} else if method == MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC {
260
shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
261
} else if method == MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE {
262
shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
263
}
264
265
// Debanding is tied to tonemapping in the shader, cannot run without it.
266
if key.contains(MeshPipelineKey::DEBAND_DITHER) {
267
shader_defs.push("DEBAND_DITHER".into());
268
}
269
}
270
271
if key.contains(MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION) {
272
shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
273
}
274
275
if key.contains(MeshPipelineKey::ENVIRONMENT_MAP) {
276
shader_defs.push("ENVIRONMENT_MAP".into());
277
}
278
279
if key.contains(MeshPipelineKey::IRRADIANCE_VOLUME) {
280
shader_defs.push("IRRADIANCE_VOLUME".into());
281
}
282
283
if key.contains(MeshPipelineKey::NORMAL_PREPASS) {
284
shader_defs.push("NORMAL_PREPASS".into());
285
}
286
287
if key.contains(MeshPipelineKey::DEPTH_PREPASS) {
288
shader_defs.push("DEPTH_PREPASS".into());
289
}
290
291
if key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
292
shader_defs.push("MOTION_VECTOR_PREPASS".into());
293
}
294
295
if key.contains(MeshPipelineKey::SCREEN_SPACE_REFLECTIONS) {
296
shader_defs.push("SCREEN_SPACE_REFLECTIONS".into());
297
}
298
299
if key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
300
shader_defs.push("HAS_PREVIOUS_SKIN".into());
301
}
302
303
if key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
304
shader_defs.push("HAS_PREVIOUS_MORPH".into());
305
}
306
307
if key.contains(MeshPipelineKey::DISTANCE_FOG) {
308
shader_defs.push("DISTANCE_FOG".into());
309
}
310
if key.contains(MeshPipelineKey::ATMOSPHERE) {
311
shader_defs.push("ATMOSPHERE".into());
312
}
313
314
// Always true, since we're in the deferred lighting pipeline
315
shader_defs.push("DEFERRED_PREPASS".into());
316
317
let shadow_filter_method =
318
key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
319
if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
320
shader_defs.push("SHADOW_FILTER_METHOD_HARDWARE_2X2".into());
321
} else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN {
322
shader_defs.push("SHADOW_FILTER_METHOD_GAUSSIAN".into());
323
} else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL {
324
shader_defs.push("SHADOW_FILTER_METHOD_TEMPORAL".into());
325
}
326
if self.mesh_pipeline.binding_arrays_are_usable {
327
shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
328
shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
329
}
330
331
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
332
shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());
333
334
let layout = self.mesh_pipeline.get_view_layout(key.into());
335
RenderPipelineDescriptor {
336
label: Some("deferred_lighting_pipeline".into()),
337
layout: vec![
338
layout.main_layout.clone(),
339
layout.binding_array_layout.clone(),
340
self.bind_group_layout_2.clone(),
341
],
342
vertex: VertexState {
343
shader: self.deferred_lighting_shader.clone(),
344
shader_defs: shader_defs.clone(),
345
..default()
346
},
347
fragment: Some(FragmentState {
348
shader: self.deferred_lighting_shader.clone(),
349
shader_defs,
350
targets: vec![Some(ColorTargetState {
351
format: if key.contains(MeshPipelineKey::HDR) {
352
ViewTarget::TEXTURE_FORMAT_HDR
353
} else {
354
TextureFormat::bevy_default()
355
},
356
blend: None,
357
write_mask: ColorWrites::ALL,
358
})],
359
..default()
360
}),
361
depth_stencil: Some(DepthStencilState {
362
format: DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT,
363
depth_write_enabled: false,
364
depth_compare: CompareFunction::Equal,
365
stencil: StencilState {
366
front: StencilFaceState::IGNORE,
367
back: StencilFaceState::IGNORE,
368
read_mask: 0,
369
write_mask: 0,
370
},
371
bias: DepthBiasState {
372
constant: 0,
373
slope_scale: 0.0,
374
clamp: 0.0,
375
},
376
}),
377
..default()
378
}
379
}
380
}
381
382
pub fn init_deferred_lighting_layout(
383
mut commands: Commands,
384
mesh_pipeline: Res<MeshPipeline>,
385
asset_server: Res<AssetServer>,
386
) {
387
let layout = BindGroupLayoutDescriptor::new(
388
"deferred_lighting_layout",
389
&BindGroupLayoutEntries::single(
390
ShaderStages::VERTEX_FRAGMENT,
391
uniform_buffer::<PbrDeferredLightingDepthId>(false),
392
),
393
);
394
commands.insert_resource(DeferredLightingLayout {
395
mesh_pipeline: mesh_pipeline.clone(),
396
bind_group_layout_2: layout,
397
deferred_lighting_shader: load_embedded_asset!(
398
asset_server.as_ref(),
399
"deferred_lighting.wgsl"
400
),
401
});
402
}
403
404
pub fn insert_deferred_lighting_pass_id_component(
405
mut commands: Commands,
406
views: Query<Entity, (With<DeferredPrepass>, Without<PbrDeferredLightingDepthId>)>,
407
) {
408
for entity in views.iter() {
409
commands
410
.entity(entity)
411
.insert(PbrDeferredLightingDepthId::default());
412
}
413
}
414
415
pub fn prepare_deferred_lighting_pipelines(
416
mut commands: Commands,
417
pipeline_cache: Res<PipelineCache>,
418
mut pipelines: ResMut<SpecializedRenderPipelines<DeferredLightingLayout>>,
419
deferred_lighting_layout: Res<DeferredLightingLayout>,
420
views: Query<(
421
Entity,
422
&ExtractedView,
423
Option<&Tonemapping>,
424
Option<&DebandDither>,
425
Option<&ShadowFilteringMethod>,
426
(
427
Has<ScreenSpaceAmbientOcclusion>,
428
Has<ScreenSpaceReflectionsUniform>,
429
Has<DistanceFog>,
430
),
431
(
432
Has<NormalPrepass>,
433
Has<DepthPrepass>,
434
Has<MotionVectorPrepass>,
435
Has<DeferredPrepass>,
436
),
437
Has<RenderViewLightProbes<EnvironmentMapLight>>,
438
Has<RenderViewLightProbes<IrradianceVolume>>,
439
Has<SkipDeferredLighting>,
440
Has<ExtractedAtmosphere>,
441
)>,
442
) {
443
for (
444
entity,
445
view,
446
tonemapping,
447
dither,
448
shadow_filter_method,
449
(ssao, ssr, distance_fog),
450
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
451
has_environment_maps,
452
has_irradiance_volumes,
453
skip_deferred_lighting,
454
has_atmosphere,
455
) in &views
456
{
457
// If there is no deferred prepass or we want to skip the deferred lighting pass,
458
// remove the old pipeline if there was one. This handles the case in which a
459
// view using deferred stops using it.
460
if !deferred_prepass || skip_deferred_lighting {
461
commands.entity(entity).remove::<DeferredLightingPipeline>();
462
continue;
463
}
464
465
let mut view_key = MeshPipelineKey::from_hdr(view.hdr);
466
467
if normal_prepass {
468
view_key |= MeshPipelineKey::NORMAL_PREPASS;
469
}
470
471
if depth_prepass {
472
view_key |= MeshPipelineKey::DEPTH_PREPASS;
473
}
474
475
if motion_vector_prepass {
476
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
477
}
478
479
if has_atmosphere {
480
view_key |= MeshPipelineKey::ATMOSPHERE;
481
}
482
483
if view.invert_culling {
484
view_key |= MeshPipelineKey::INVERT_CULLING;
485
}
486
487
// Always true, since we're in the deferred lighting pipeline
488
view_key |= MeshPipelineKey::DEFERRED_PREPASS;
489
490
if !view.hdr {
491
if let Some(tonemapping) = tonemapping {
492
view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
493
view_key |= match tonemapping {
494
Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
495
Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
496
Tonemapping::ReinhardLuminance => {
497
MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE
498
}
499
Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
500
Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
501
Tonemapping::SomewhatBoringDisplayTransform => {
502
MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
503
}
504
Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
505
Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
506
};
507
}
508
if let Some(DebandDither::Enabled) = dither {
509
view_key |= MeshPipelineKey::DEBAND_DITHER;
510
}
511
}
512
513
if ssao {
514
view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
515
}
516
if ssr {
517
view_key |= MeshPipelineKey::SCREEN_SPACE_REFLECTIONS;
518
}
519
if distance_fog {
520
view_key |= MeshPipelineKey::DISTANCE_FOG;
521
}
522
523
// We don't need to check to see whether the environment map is loaded
524
// because [`gather_light_probes`] already checked that for us before
525
// adding the [`RenderViewEnvironmentMaps`] component.
526
if has_environment_maps {
527
view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
528
}
529
530
if has_irradiance_volumes {
531
view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
532
}
533
534
match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
535
ShadowFilteringMethod::Hardware2x2 => {
536
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
537
}
538
ShadowFilteringMethod::Gaussian => {
539
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
540
}
541
ShadowFilteringMethod::Temporal => {
542
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
543
}
544
}
545
546
let pipeline_id =
547
pipelines.specialize(&pipeline_cache, &deferred_lighting_layout, view_key);
548
549
commands
550
.entity(entity)
551
.insert(DeferredLightingPipeline { pipeline_id });
552
}
553
}
554
555
/// Component to skip running the deferred lighting pass in [`deferred_lighting`] for a specific view.
556
///
557
/// This works like [`crate::PbrPlugin::add_default_deferred_lighting_plugin`], but is per-view instead of global.
558
///
559
/// Useful for cases where you want to generate a gbuffer, but skip the built-in deferred lighting pass
560
/// to run your own custom lighting pass instead.
561
///
562
/// Insert this component in the render world only.
563
#[derive(Component, Clone, Copy, Default)]
564
pub struct SkipDeferredLighting;
565
566