bevy_solari/realtime/
mod.rs

1mod extract;
2mod node;
3mod prepare;
4
5use crate::SolariPlugins;
6use bevy_app::{App, Plugin};
7use bevy_asset::embedded_asset;
8use bevy_core_pipeline::{
9    core_3d::graph::{Core3d, Node3d},
10    prepass::{
11        DeferredPrepass, DeferredPrepassDoubleBuffer, DepthPrepass, DepthPrepassDoubleBuffer,
12        MotionVectorPrepass,
13    },
14};
15use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs};
16use bevy_pbr::DefaultOpaqueRendererMethod;
17use bevy_reflect::{std_traits::ReflectDefault, Reflect};
18use bevy_render::{
19    render_graph::{RenderGraphExt, ViewNodeRunner},
20    renderer::RenderDevice,
21    view::Hdr,
22    ExtractSchedule, Render, RenderApp, RenderSystems,
23};
24use bevy_shader::load_shader_library;
25use extract::extract_solari_lighting;
26use node::SolariLightingNode;
27use prepare::prepare_solari_lighting_resources;
28use tracing::warn;
29
30/// Raytraced direct and indirect lighting.
31///
32/// When using this plugin, it's highly recommended to set `shadows_enabled: false` on all lights, as Solari replaces
33/// traditional shadow mapping.
34pub struct SolariLightingPlugin;
35
36impl Plugin for SolariLightingPlugin {
37    fn build(&self, app: &mut App) {
38        load_shader_library!(app, "gbuffer_utils.wgsl");
39        load_shader_library!(app, "presample_light_tiles.wgsl");
40        embedded_asset!(app, "restir_di.wgsl");
41        embedded_asset!(app, "restir_gi.wgsl");
42        load_shader_library!(app, "specular_gi.wgsl");
43        load_shader_library!(app, "world_cache_query.wgsl");
44        embedded_asset!(app, "world_cache_compact.wgsl");
45        embedded_asset!(app, "world_cache_update.wgsl");
46
47        #[cfg(all(feature = "dlss", not(feature = "force_disable_dlss")))]
48        embedded_asset!(app, "resolve_dlss_rr_textures.wgsl");
49
50        app.insert_resource(DefaultOpaqueRendererMethod::deferred());
51    }
52
53    fn finish(&self, app: &mut App) {
54        let render_app = app.sub_app_mut(RenderApp);
55
56        let render_device = render_app.world().resource::<RenderDevice>();
57        let features = render_device.features();
58        if !features.contains(SolariPlugins::required_wgpu_features()) {
59            warn!(
60                "SolariLightingPlugin not loaded. GPU lacks support for required features: {:?}.",
61                SolariPlugins::required_wgpu_features().difference(features)
62            );
63            return;
64        }
65
66        render_app
67            .add_systems(ExtractSchedule, extract_solari_lighting)
68            .add_systems(
69                Render,
70                prepare_solari_lighting_resources.in_set(RenderSystems::PrepareResources),
71            )
72            .add_render_graph_node::<ViewNodeRunner<SolariLightingNode>>(
73                Core3d,
74                node::graph::SolariLightingNode,
75            )
76            .add_render_graph_edges(
77                Core3d,
78                (
79                    Node3d::EndPrepasses,
80                    node::graph::SolariLightingNode,
81                    Node3d::EndMainPass,
82                ),
83            );
84    }
85}
86
87/// A component for a 3d camera entity to enable the Solari raytraced lighting system.
88///
89/// Must be used with `CameraMainTextureUsages::default().with(TextureUsages::STORAGE_BINDING)`, and
90/// `Msaa::Off`.
91#[derive(Component, Reflect, Clone)]
92#[reflect(Component, Default, Clone)]
93#[require(
94    Hdr,
95    DeferredPrepass,
96    DepthPrepass,
97    MotionVectorPrepass,
98    DeferredPrepassDoubleBuffer,
99    DepthPrepassDoubleBuffer
100)]
101pub struct SolariLighting {
102    /// Set to true to delete the saved temporal history (past frames).
103    ///
104    /// Useful for preventing ghosting when the history is no longer
105    /// representative of the current frame, such as in sudden camera cuts.
106    ///
107    /// After setting this to true, it will automatically be toggled
108    /// back to false at the end of the frame.
109    pub reset: bool,
110}
111
112impl Default for SolariLighting {
113    fn default() -> Self {
114        Self {
115            reset: true, // No temporal history on the first frame
116        }
117    }
118}