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