bevy_pbr/
lib.rs

1#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![forbid(unsafe_code)]
4#![doc(
5    html_logo_url = "https://bevy.org/assets/icon.png",
6    html_favicon_url = "https://bevy.org/assets/icon.png"
7)]
8
9extern crate alloc;
10
11#[cfg(feature = "meshlet")]
12mod meshlet;
13pub mod wireframe;
14
15/// Experimental features that are not yet finished. Please report any issues you encounter!
16///
17/// Expect bugs, missing features, compatibility issues, low performance, and/or future breaking changes.
18#[cfg(feature = "meshlet")]
19pub mod experimental {
20    /// Render high-poly 3d meshes using an efficient GPU-driven method.
21    /// See [`MeshletPlugin`](meshlet::MeshletPlugin) and [`MeshletMesh`](meshlet::MeshletMesh) for details.
22    pub mod meshlet {
23        pub use crate::meshlet::*;
24    }
25}
26
27mod atmosphere;
28mod cluster;
29mod components;
30pub mod decal;
31pub mod deferred;
32mod extended_material;
33mod fog;
34mod light_probe;
35mod lightmap;
36mod material;
37mod material_bind_groups;
38mod mesh_material;
39mod parallax;
40mod pbr_material;
41mod prepass;
42mod render;
43mod ssao;
44mod ssr;
45mod volumetric_fog;
46
47use bevy_color::{Color, LinearRgba};
48
49pub use atmosphere::*;
50use bevy_light::{
51    AmbientLight, DirectionalLight, PointLight, ShadowFilteringMethod, SimulationLightSystems,
52    SpotLight,
53};
54use bevy_shader::{load_shader_library, ShaderRef};
55pub use cluster::*;
56pub use components::*;
57pub use decal::clustered::ClusteredDecalPlugin;
58pub use extended_material::*;
59pub use fog::*;
60pub use light_probe::*;
61pub use lightmap::*;
62pub use material::*;
63pub use material_bind_groups::*;
64pub use mesh_material::*;
65pub use parallax::*;
66pub use pbr_material::*;
67pub use prepass::*;
68pub use render::*;
69pub use ssao::*;
70pub use ssr::*;
71pub use volumetric_fog::VolumetricFogPlugin;
72
73/// The PBR prelude.
74///
75/// This includes the most common types in this crate, re-exported for your convenience.
76pub mod prelude {
77    #[doc(hidden)]
78    pub use crate::{
79        fog::{DistanceFog, FogFalloff},
80        material::{Material, MaterialPlugin},
81        mesh_material::MeshMaterial3d,
82        parallax::ParallaxMappingMethod,
83        pbr_material::StandardMaterial,
84        ssao::ScreenSpaceAmbientOcclusionPlugin,
85    };
86}
87
88pub mod graph {
89    use bevy_render::render_graph::RenderLabel;
90
91    /// Render graph nodes specific to 3D PBR rendering.
92    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
93    pub enum NodePbr {
94        /// Label for the shadow pass node that draws meshes that were visible
95        /// from the light last frame.
96        EarlyShadowPass,
97        /// Label for the shadow pass node that draws meshes that became visible
98        /// from the light this frame.
99        LateShadowPass,
100        /// Label for the screen space ambient occlusion render node.
101        ScreenSpaceAmbientOcclusion,
102        DeferredLightingPass,
103        /// Label for the volumetric lighting pass.
104        VolumetricFog,
105        /// Label for the shader that transforms and culls meshes that were
106        /// visible last frame.
107        EarlyGpuPreprocess,
108        /// Label for the shader that transforms and culls meshes that became
109        /// visible this frame.
110        LateGpuPreprocess,
111        /// Label for the screen space reflections pass.
112        ScreenSpaceReflections,
113        /// Label for the node that builds indirect draw parameters for meshes
114        /// that were visible last frame.
115        EarlyPrepassBuildIndirectParameters,
116        /// Label for the node that builds indirect draw parameters for meshes
117        /// that became visible this frame.
118        LatePrepassBuildIndirectParameters,
119        /// Label for the node that builds indirect draw parameters for the main
120        /// rendering pass, containing all meshes that are visible this frame.
121        MainBuildIndirectParameters,
122        ClearIndirectParametersMetadata,
123    }
124}
125
126use crate::{deferred::DeferredPbrLightingPlugin, graph::NodePbr};
127use bevy_app::prelude::*;
128use bevy_asset::{AssetApp, AssetPath, Assets, Handle, RenderAssetUsages};
129use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
130use bevy_ecs::prelude::*;
131#[cfg(feature = "bluenoise_texture")]
132use bevy_image::{CompressedImageFormats, ImageType};
133use bevy_image::{Image, ImageSampler};
134use bevy_render::{
135    alpha::AlphaMode,
136    camera::sort_cameras,
137    extract_resource::ExtractResourcePlugin,
138    render_graph::RenderGraph,
139    render_resource::{
140        Extent3d, TextureDataOrder, TextureDescriptor, TextureDimension, TextureFormat,
141        TextureUsages,
142    },
143    sync_component::SyncComponentPlugin,
144    ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderStartup, RenderSystems,
145};
146
147use std::path::PathBuf;
148
149fn shader_ref(path: PathBuf) -> ShaderRef {
150    ShaderRef::Path(AssetPath::from_path_buf(path).with_source("embedded"))
151}
152
153pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 18;
154pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 19;
155
156/// Sets up the entire PBR infrastructure of bevy.
157pub struct PbrPlugin {
158    /// Controls if the prepass is enabled for the [`StandardMaterial`].
159    /// For more information about what a prepass is, see the [`bevy_core_pipeline::prepass`] docs.
160    pub prepass_enabled: bool,
161    /// Controls if [`DeferredPbrLightingPlugin`] is added.
162    pub add_default_deferred_lighting_plugin: bool,
163    /// Controls if GPU [`MeshUniform`] building is enabled.
164    ///
165    /// This requires compute shader support and so will be forcibly disabled if
166    /// the platform doesn't support those.
167    pub use_gpu_instance_buffer_builder: bool,
168    /// Debugging flags that can optionally be set when constructing the renderer.
169    pub debug_flags: RenderDebugFlags,
170}
171
172impl Default for PbrPlugin {
173    fn default() -> Self {
174        Self {
175            prepass_enabled: true,
176            add_default_deferred_lighting_plugin: true,
177            use_gpu_instance_buffer_builder: true,
178            debug_flags: RenderDebugFlags::default(),
179        }
180    }
181}
182
183/// A resource that stores the spatio-temporal blue noise texture.
184#[derive(Resource)]
185pub struct Bluenoise {
186    /// Texture handle for spatio-temporal blue noise
187    pub texture: Handle<Image>,
188}
189
190impl Plugin for PbrPlugin {
191    fn build(&self, app: &mut App) {
192        load_shader_library!(app, "render/pbr_types.wgsl");
193        load_shader_library!(app, "render/pbr_bindings.wgsl");
194        load_shader_library!(app, "render/utils.wgsl");
195        load_shader_library!(app, "render/clustered_forward.wgsl");
196        load_shader_library!(app, "render/pbr_lighting.wgsl");
197        load_shader_library!(app, "render/pbr_transmission.wgsl");
198        load_shader_library!(app, "render/shadows.wgsl");
199        load_shader_library!(app, "deferred/pbr_deferred_types.wgsl");
200        load_shader_library!(app, "deferred/pbr_deferred_functions.wgsl");
201        load_shader_library!(app, "render/shadow_sampling.wgsl");
202        load_shader_library!(app, "render/pbr_functions.wgsl");
203        load_shader_library!(app, "render/rgb9e5.wgsl");
204        load_shader_library!(app, "render/pbr_ambient.wgsl");
205        load_shader_library!(app, "render/pbr_fragment.wgsl");
206        load_shader_library!(app, "render/pbr.wgsl");
207        load_shader_library!(app, "render/pbr_prepass_functions.wgsl");
208        load_shader_library!(app, "render/pbr_prepass.wgsl");
209        load_shader_library!(app, "render/parallax_mapping.wgsl");
210        load_shader_library!(app, "render/view_transformations.wgsl");
211
212        // Setup dummy shaders for when MeshletPlugin is not used to prevent shader import errors.
213        load_shader_library!(app, "meshlet/dummy_visibility_buffer_resolve.wgsl");
214
215        app.register_asset_reflect::<StandardMaterial>()
216            .init_resource::<DefaultOpaqueRendererMethod>()
217            .add_plugins((
218                MeshRenderPlugin {
219                    use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
220                    debug_flags: self.debug_flags,
221                },
222                MaterialsPlugin {
223                    debug_flags: self.debug_flags,
224                },
225                MaterialPlugin::<StandardMaterial> {
226                    prepass_enabled: self.prepass_enabled,
227                    debug_flags: self.debug_flags,
228                    ..Default::default()
229                },
230                ScreenSpaceAmbientOcclusionPlugin,
231                FogPlugin,
232                ExtractResourcePlugin::<DefaultOpaqueRendererMethod>::default(),
233                SyncComponentPlugin::<ShadowFilteringMethod>::default(),
234                LightmapPlugin,
235                LightProbePlugin,
236                GpuMeshPreprocessPlugin {
237                    use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
238                },
239                VolumetricFogPlugin,
240                ScreenSpaceReflectionsPlugin,
241                ClusteredDecalPlugin,
242            ))
243            .add_plugins((
244                decal::ForwardDecalPlugin,
245                SyncComponentPlugin::<DirectionalLight>::default(),
246                SyncComponentPlugin::<PointLight>::default(),
247                SyncComponentPlugin::<SpotLight>::default(),
248                SyncComponentPlugin::<AmbientLight>::default(),
249            ))
250            .add_plugins(AtmospherePlugin)
251            .configure_sets(
252                PostUpdate,
253                (
254                    SimulationLightSystems::AddClusters,
255                    SimulationLightSystems::AssignLightsToClusters,
256                )
257                    .chain(),
258            );
259
260        if self.add_default_deferred_lighting_plugin {
261            app.add_plugins(DeferredPbrLightingPlugin);
262        }
263
264        // Initialize the default material handle.
265        app.world_mut()
266            .resource_mut::<Assets<StandardMaterial>>()
267            .insert(
268                &Handle::<StandardMaterial>::default(),
269                StandardMaterial {
270                    base_color: Color::srgb(1.0, 0.0, 0.5),
271                    ..Default::default()
272                },
273            )
274            .unwrap();
275
276        let has_bluenoise = app
277            .get_sub_app(RenderApp)
278            .is_some_and(|render_app| render_app.world().is_resource_added::<Bluenoise>());
279
280        if !has_bluenoise {
281            let mut images = app.world_mut().resource_mut::<Assets<Image>>();
282            #[cfg(feature = "bluenoise_texture")]
283            let handle = {
284                let image = Image::from_buffer(
285                    include_bytes!("bluenoise/stbn.ktx2"),
286                    ImageType::Extension("ktx2"),
287                    CompressedImageFormats::NONE,
288                    false,
289                    ImageSampler::Default,
290                    RenderAssetUsages::RENDER_WORLD,
291                )
292                .expect("Failed to decode embedded blue-noise texture");
293                images.add(image)
294            };
295
296            #[cfg(not(feature = "bluenoise_texture"))]
297            let handle = { images.add(stbn_placeholder()) };
298
299            if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
300                render_app
301                    .world_mut()
302                    .insert_resource(Bluenoise { texture: handle });
303            }
304        }
305
306        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
307            return;
308        };
309
310        // Extract the required data from the main world
311        render_app
312            .add_systems(
313                RenderStartup,
314                (
315                    init_shadow_samplers,
316                    init_global_clusterable_object_meta,
317                    init_fallback_bindless_resources,
318                ),
319            )
320            .add_systems(
321                ExtractSchedule,
322                (
323                    extract_clusters,
324                    extract_lights,
325                    extract_ambient_light_resource,
326                    extract_ambient_light,
327                    extract_shadow_filtering_method,
328                    late_sweep_material_instances,
329                ),
330            )
331            .add_systems(
332                Render,
333                (
334                    prepare_lights
335                        .in_set(RenderSystems::ManageViews)
336                        .after(sort_cameras),
337                    prepare_clusters.in_set(RenderSystems::PrepareResources),
338                ),
339            )
340            .init_resource::<LightMeta>()
341            .init_resource::<RenderMaterialBindings>();
342
343        render_app.world_mut().add_observer(add_light_view_entities);
344        render_app
345            .world_mut()
346            .add_observer(remove_light_view_entities);
347        render_app.world_mut().add_observer(extracted_light_removed);
348
349        let early_shadow_pass_node = EarlyShadowPassNode::from_world(render_app.world_mut());
350        let late_shadow_pass_node = LateShadowPassNode::from_world(render_app.world_mut());
351        let mut graph = render_app.world_mut().resource_mut::<RenderGraph>();
352        let draw_3d_graph = graph.get_sub_graph_mut(Core3d).unwrap();
353        draw_3d_graph.add_node(NodePbr::EarlyShadowPass, early_shadow_pass_node);
354        draw_3d_graph.add_node(NodePbr::LateShadowPass, late_shadow_pass_node);
355        draw_3d_graph.add_node_edges((
356            NodePbr::EarlyShadowPass,
357            NodePbr::LateShadowPass,
358            Node3d::StartMainPass,
359        ));
360    }
361
362    fn finish(&self, app: &mut App) {
363        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
364            return;
365        };
366
367        let global_cluster_settings = make_global_cluster_settings(render_app.world());
368        app.insert_resource(global_cluster_settings);
369    }
370}
371
372pub fn stbn_placeholder() -> Image {
373    let format = TextureFormat::Rgba8Unorm;
374    let data = vec![255, 0, 255, 255];
375    Image {
376        data: Some(data),
377        data_order: TextureDataOrder::default(),
378        texture_descriptor: TextureDescriptor {
379            size: Extent3d::default(),
380            format,
381            dimension: TextureDimension::D2,
382            label: None,
383            mip_level_count: 1,
384            sample_count: 1,
385            usage: TextureUsages::TEXTURE_BINDING,
386            view_formats: &[],
387        },
388        sampler: ImageSampler::Default,
389        texture_view_descriptor: None,
390        asset_usage: RenderAssetUsages::RENDER_WORLD,
391        copy_on_resize: false,
392    }
393}