bevy_pbr/deferred/
mod.rs

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/// Component with a `depth_id` for specifying which corresponding materials should be rendered by this specific PBR deferred lighting pass.
41///
42/// Will be automatically added to entities with the [`DeferredPrepass`] component that don't already have a [`PbrDeferredLightingDepthId`].
43#[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        // Let the shader code know that it's running in a deferred pipeline.
247        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            // Debanding is tied to tonemapping in the shader, cannot run without it.
284            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        // Always true, since we're in the deferred lighting pipeline
330        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 there is no deferred prepass or we want to skip the deferred lighting pass,
472        // remove the old pipeline if there was one. This handles the case in which a
473        // view using deferred stops using it.
474        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        // Always true, since we're in the deferred lighting pipeline
494        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        // We don't need to check to see whether the environment map is loaded
530        // because [`gather_light_probes`] already checked that for us before
531        // adding the [`RenderViewEnvironmentMaps`] component.
532        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/// Component to skip running the deferred lighting pass in [`DeferredOpaquePass3dPbrLightingNode`] for a specific view.
562///
563/// This works like [`crate::PbrPlugin::add_default_deferred_lighting_plugin`], but is per-view instead of global.
564///
565/// Useful for cases where you want to generate a gbuffer, but skip the built-in deferred lighting pass
566/// to run your own custom lighting pass instead.
567///
568/// Insert this component in the render world only.
569#[derive(Component, Clone, Copy, Default)]
570pub struct SkipDeferredLighting;