bevy_pbr/
lib.rs

1// FIXME(15321): solve CI failures, then replace with `#![expect()]`.
2#![allow(missing_docs, reason = "Not all docs are written yet, see #3492.")]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4#![deny(unsafe_code)]
5#![doc(
6    html_logo_url = "https://bevyengine.org/assets/icon.png",
7    html_favicon_url = "https://bevyengine.org/assets/icon.png"
8)]
9
10extern crate alloc;
11
12#[cfg(feature = "meshlet")]
13mod meshlet;
14pub mod wireframe;
15
16/// Experimental features that are not yet finished. Please report any issues you encounter!
17///
18/// Expect bugs, missing features, compatibility issues, low performance, and/or future breaking changes.
19#[cfg(feature = "meshlet")]
20pub mod experimental {
21    /// Render high-poly 3d meshes using an efficient GPU-driven method.
22    /// See [`MeshletPlugin`](meshlet::MeshletPlugin) and [`MeshletMesh`](meshlet::MeshletMesh) for details.
23    pub mod meshlet {
24        pub use crate::meshlet::*;
25    }
26}
27
28mod bundle;
29mod cluster;
30pub mod deferred;
31mod extended_material;
32mod fog;
33mod light;
34mod light_probe;
35mod lightmap;
36mod material;
37mod mesh_material;
38mod parallax;
39mod pbr_material;
40mod prepass;
41mod render;
42mod ssao;
43mod ssr;
44mod volumetric_fog;
45
46use bevy_color::{Color, LinearRgba};
47use core::marker::PhantomData;
48
49pub use bundle::*;
50pub use cluster::*;
51pub use extended_material::*;
52pub use fog::*;
53pub use light::*;
54pub use light_probe::*;
55pub use lightmap::*;
56pub use material::*;
57pub use mesh_material::*;
58pub use parallax::*;
59pub use pbr_material::*;
60pub use prepass::*;
61pub use render::*;
62pub use ssao::*;
63pub use ssr::*;
64#[allow(deprecated)]
65pub use volumetric_fog::{
66    FogVolume, FogVolumeBundle, VolumetricFog, VolumetricFogPlugin, VolumetricFogSettings,
67    VolumetricLight,
68};
69
70/// The PBR prelude.
71///
72/// This includes the most common types in this crate, re-exported for your convenience.
73#[expect(deprecated)]
74pub mod prelude {
75    #[doc(hidden)]
76    pub use crate::{
77        bundle::{
78            DirectionalLightBundle, MaterialMeshBundle, PbrBundle, PointLightBundle,
79            SpotLightBundle,
80        },
81        fog::{DistanceFog, FogFalloff},
82        light::{light_consts, AmbientLight, DirectionalLight, PointLight, SpotLight},
83        light_probe::{
84            environment_map::{EnvironmentMapLight, ReflectionProbeBundle},
85            LightProbe,
86        },
87        material::{Material, MaterialPlugin},
88        mesh_material::MeshMaterial3d,
89        parallax::ParallaxMappingMethod,
90        pbr_material::StandardMaterial,
91        ssao::ScreenSpaceAmbientOcclusionPlugin,
92    };
93}
94
95pub mod graph {
96    use bevy_render::render_graph::RenderLabel;
97
98    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
99    pub enum NodePbr {
100        /// Label for the shadow pass node.
101        ShadowPass,
102        /// Label for the screen space ambient occlusion render node.
103        ScreenSpaceAmbientOcclusion,
104        DeferredLightingPass,
105        /// Label for the volumetric lighting pass.
106        VolumetricFog,
107        /// Label for the compute shader instance data building pass.
108        GpuPreprocess,
109        /// Label for the screen space reflections pass.
110        ScreenSpaceReflections,
111    }
112}
113
114use crate::{deferred::DeferredPbrLightingPlugin, graph::NodePbr};
115use bevy_app::prelude::*;
116use bevy_asset::{load_internal_asset, AssetApp, Assets, Handle};
117use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
118use bevy_ecs::prelude::*;
119use bevy_image::Image;
120use bevy_render::{
121    alpha::AlphaMode,
122    camera::{
123        CameraProjection, CameraUpdateSystem, OrthographicProjection, PerspectiveProjection,
124        Projection,
125    },
126    extract_component::ExtractComponentPlugin,
127    extract_resource::ExtractResourcePlugin,
128    render_asset::prepare_assets,
129    render_graph::RenderGraph,
130    render_resource::Shader,
131    sync_component::SyncComponentPlugin,
132    texture::GpuImage,
133    view::{check_visibility, VisibilitySystems},
134    ExtractSchedule, Render, RenderApp, RenderSet,
135};
136
137use bevy_transform::TransformSystem;
138
139pub const PBR_TYPES_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(1708015359337029744);
140pub const PBR_BINDINGS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(5635987986427308186);
141pub const UTILS_HANDLE: Handle<Shader> = Handle::weak_from_u128(1900548483293416725);
142pub const CLUSTERED_FORWARD_HANDLE: Handle<Shader> = Handle::weak_from_u128(166852093121196815);
143pub const PBR_LIGHTING_HANDLE: Handle<Shader> = Handle::weak_from_u128(14170772752254856967);
144pub const PBR_TRANSMISSION_HANDLE: Handle<Shader> = Handle::weak_from_u128(77319684653223658032);
145pub const SHADOWS_HANDLE: Handle<Shader> = Handle::weak_from_u128(11350275143789590502);
146pub const SHADOW_SAMPLING_HANDLE: Handle<Shader> = Handle::weak_from_u128(3145627513789590502);
147pub const PBR_FRAGMENT_HANDLE: Handle<Shader> = Handle::weak_from_u128(2295049283805286543);
148pub const PBR_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(4805239651767701046);
149pub const PBR_PREPASS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(9407115064344201137);
150pub const PBR_FUNCTIONS_HANDLE: Handle<Shader> = Handle::weak_from_u128(16550102964439850292);
151pub const PBR_AMBIENT_HANDLE: Handle<Shader> = Handle::weak_from_u128(2441520459096337034);
152pub const PARALLAX_MAPPING_SHADER_HANDLE: Handle<Shader> =
153    Handle::weak_from_u128(17035894873630133905);
154pub const VIEW_TRANSFORMATIONS_SHADER_HANDLE: Handle<Shader> =
155    Handle::weak_from_u128(2098345702398750291);
156pub const PBR_PREPASS_FUNCTIONS_SHADER_HANDLE: Handle<Shader> =
157    Handle::weak_from_u128(73204817249182637);
158pub const PBR_DEFERRED_TYPES_HANDLE: Handle<Shader> = Handle::weak_from_u128(3221241127431430599);
159pub const PBR_DEFERRED_FUNCTIONS_HANDLE: Handle<Shader> = Handle::weak_from_u128(72019026415438599);
160pub const RGB9E5_FUNCTIONS_HANDLE: Handle<Shader> = Handle::weak_from_u128(2659010996143919192);
161const MESHLET_VISIBILITY_BUFFER_RESOLVE_SHADER_HANDLE: Handle<Shader> =
162    Handle::weak_from_u128(2325134235233421);
163
164pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 23;
165pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 24;
166
167/// Sets up the entire PBR infrastructure of bevy.
168pub struct PbrPlugin {
169    /// Controls if the prepass is enabled for the [`StandardMaterial`].
170    /// For more information about what a prepass is, see the [`bevy_core_pipeline::prepass`] docs.
171    pub prepass_enabled: bool,
172    /// Controls if [`DeferredPbrLightingPlugin`] is added.
173    pub add_default_deferred_lighting_plugin: bool,
174    /// Controls if GPU [`MeshUniform`] building is enabled.
175    ///
176    /// This requires compute shader support and so will be forcibly disabled if
177    /// the platform doesn't support those.
178    pub use_gpu_instance_buffer_builder: bool,
179}
180
181impl Default for PbrPlugin {
182    fn default() -> Self {
183        Self {
184            prepass_enabled: true,
185            add_default_deferred_lighting_plugin: true,
186            use_gpu_instance_buffer_builder: true,
187        }
188    }
189}
190
191impl Plugin for PbrPlugin {
192    fn build(&self, app: &mut App) {
193        load_internal_asset!(
194            app,
195            PBR_TYPES_SHADER_HANDLE,
196            "render/pbr_types.wgsl",
197            Shader::from_wgsl
198        );
199        load_internal_asset!(
200            app,
201            PBR_BINDINGS_SHADER_HANDLE,
202            "render/pbr_bindings.wgsl",
203            Shader::from_wgsl
204        );
205        load_internal_asset!(app, UTILS_HANDLE, "render/utils.wgsl", Shader::from_wgsl);
206        load_internal_asset!(
207            app,
208            CLUSTERED_FORWARD_HANDLE,
209            "render/clustered_forward.wgsl",
210            Shader::from_wgsl
211        );
212        load_internal_asset!(
213            app,
214            PBR_LIGHTING_HANDLE,
215            "render/pbr_lighting.wgsl",
216            Shader::from_wgsl
217        );
218        load_internal_asset!(
219            app,
220            PBR_TRANSMISSION_HANDLE,
221            "render/pbr_transmission.wgsl",
222            Shader::from_wgsl
223        );
224        load_internal_asset!(
225            app,
226            SHADOWS_HANDLE,
227            "render/shadows.wgsl",
228            Shader::from_wgsl
229        );
230        load_internal_asset!(
231            app,
232            PBR_DEFERRED_TYPES_HANDLE,
233            "deferred/pbr_deferred_types.wgsl",
234            Shader::from_wgsl
235        );
236        load_internal_asset!(
237            app,
238            PBR_DEFERRED_FUNCTIONS_HANDLE,
239            "deferred/pbr_deferred_functions.wgsl",
240            Shader::from_wgsl
241        );
242        load_internal_asset!(
243            app,
244            SHADOW_SAMPLING_HANDLE,
245            "render/shadow_sampling.wgsl",
246            Shader::from_wgsl
247        );
248        load_internal_asset!(
249            app,
250            PBR_FUNCTIONS_HANDLE,
251            "render/pbr_functions.wgsl",
252            Shader::from_wgsl
253        );
254        load_internal_asset!(
255            app,
256            RGB9E5_FUNCTIONS_HANDLE,
257            "render/rgb9e5.wgsl",
258            Shader::from_wgsl
259        );
260        load_internal_asset!(
261            app,
262            PBR_AMBIENT_HANDLE,
263            "render/pbr_ambient.wgsl",
264            Shader::from_wgsl
265        );
266        load_internal_asset!(
267            app,
268            PBR_FRAGMENT_HANDLE,
269            "render/pbr_fragment.wgsl",
270            Shader::from_wgsl
271        );
272        load_internal_asset!(app, PBR_SHADER_HANDLE, "render/pbr.wgsl", Shader::from_wgsl);
273        load_internal_asset!(
274            app,
275            PBR_PREPASS_FUNCTIONS_SHADER_HANDLE,
276            "render/pbr_prepass_functions.wgsl",
277            Shader::from_wgsl
278        );
279        load_internal_asset!(
280            app,
281            PBR_PREPASS_SHADER_HANDLE,
282            "render/pbr_prepass.wgsl",
283            Shader::from_wgsl
284        );
285        load_internal_asset!(
286            app,
287            PARALLAX_MAPPING_SHADER_HANDLE,
288            "render/parallax_mapping.wgsl",
289            Shader::from_wgsl
290        );
291        load_internal_asset!(
292            app,
293            VIEW_TRANSFORMATIONS_SHADER_HANDLE,
294            "render/view_transformations.wgsl",
295            Shader::from_wgsl
296        );
297        // Setup dummy shaders for when MeshletPlugin is not used to prevent shader import errors.
298        load_internal_asset!(
299            app,
300            MESHLET_VISIBILITY_BUFFER_RESOLVE_SHADER_HANDLE,
301            "meshlet/dummy_visibility_buffer_resolve.wgsl",
302            Shader::from_wgsl
303        );
304
305        app.register_asset_reflect::<StandardMaterial>()
306            .register_type::<AmbientLight>()
307            .register_type::<CascadeShadowConfig>()
308            .register_type::<Cascades>()
309            .register_type::<CascadesVisibleEntities>()
310            .register_type::<VisibleMeshEntities>()
311            .register_type::<ClusterConfig>()
312            .register_type::<CubemapVisibleEntities>()
313            .register_type::<DirectionalLight>()
314            .register_type::<DirectionalLightShadowMap>()
315            .register_type::<NotShadowCaster>()
316            .register_type::<NotShadowReceiver>()
317            .register_type::<PointLight>()
318            .register_type::<PointLightShadowMap>()
319            .register_type::<SpotLight>()
320            .register_type::<ShadowFilteringMethod>()
321            .init_resource::<AmbientLight>()
322            .init_resource::<GlobalVisibleClusterableObjects>()
323            .init_resource::<DirectionalLightShadowMap>()
324            .init_resource::<PointLightShadowMap>()
325            .register_type::<DefaultOpaqueRendererMethod>()
326            .init_resource::<DefaultOpaqueRendererMethod>()
327            .add_plugins((
328                MeshRenderPlugin {
329                    use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
330                },
331                MaterialPlugin::<StandardMaterial> {
332                    prepass_enabled: self.prepass_enabled,
333                    ..Default::default()
334                },
335                ScreenSpaceAmbientOcclusionPlugin,
336                ExtractResourcePlugin::<AmbientLight>::default(),
337                FogPlugin,
338                ExtractResourcePlugin::<DefaultOpaqueRendererMethod>::default(),
339                ExtractComponentPlugin::<ShadowFilteringMethod>::default(),
340                LightmapPlugin,
341                LightProbePlugin,
342                PbrProjectionPlugin::<Projection>::default(),
343                PbrProjectionPlugin::<PerspectiveProjection>::default(),
344                PbrProjectionPlugin::<OrthographicProjection>::default(),
345                GpuMeshPreprocessPlugin {
346                    use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
347                },
348                VolumetricFogPlugin,
349                ScreenSpaceReflectionsPlugin,
350            ))
351            .add_plugins((
352                SyncComponentPlugin::<DirectionalLight>::default(),
353                SyncComponentPlugin::<PointLight>::default(),
354                SyncComponentPlugin::<SpotLight>::default(),
355            ))
356            .configure_sets(
357                PostUpdate,
358                (
359                    SimulationLightSystems::AddClusters,
360                    SimulationLightSystems::AssignLightsToClusters,
361                )
362                    .chain(),
363            )
364            .configure_sets(
365                PostUpdate,
366                SimulationLightSystems::UpdateDirectionalLightCascades
367                    .ambiguous_with(SimulationLightSystems::UpdateDirectionalLightCascades),
368            )
369            .configure_sets(
370                PostUpdate,
371                SimulationLightSystems::CheckLightVisibility
372                    .ambiguous_with(SimulationLightSystems::CheckLightVisibility),
373            )
374            .add_systems(
375                PostUpdate,
376                (
377                    add_clusters
378                        .in_set(SimulationLightSystems::AddClusters)
379                        .after(CameraUpdateSystem),
380                    assign_objects_to_clusters
381                        .in_set(SimulationLightSystems::AssignLightsToClusters)
382                        .after(TransformSystem::TransformPropagate)
383                        .after(VisibilitySystems::CheckVisibility)
384                        .after(CameraUpdateSystem),
385                    clear_directional_light_cascades
386                        .in_set(SimulationLightSystems::UpdateDirectionalLightCascades)
387                        .after(TransformSystem::TransformPropagate)
388                        .after(CameraUpdateSystem),
389                    update_directional_light_frusta
390                        .in_set(SimulationLightSystems::UpdateLightFrusta)
391                        // This must run after CheckVisibility because it relies on `ViewVisibility`
392                        .after(VisibilitySystems::CheckVisibility)
393                        .after(TransformSystem::TransformPropagate)
394                        .after(SimulationLightSystems::UpdateDirectionalLightCascades)
395                        // We assume that no entity will be both a directional light and a spot light,
396                        // so these systems will run independently of one another.
397                        // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481.
398                        .ambiguous_with(update_spot_light_frusta),
399                    update_point_light_frusta
400                        .in_set(SimulationLightSystems::UpdateLightFrusta)
401                        .after(TransformSystem::TransformPropagate)
402                        .after(SimulationLightSystems::AssignLightsToClusters),
403                    update_spot_light_frusta
404                        .in_set(SimulationLightSystems::UpdateLightFrusta)
405                        .after(TransformSystem::TransformPropagate)
406                        .after(SimulationLightSystems::AssignLightsToClusters),
407                    check_visibility::<WithLight>.in_set(VisibilitySystems::CheckVisibility),
408                    (
409                        check_dir_light_mesh_visibility,
410                        check_point_light_mesh_visibility,
411                    )
412                        .in_set(SimulationLightSystems::CheckLightVisibility)
413                        .after(VisibilitySystems::CalculateBounds)
414                        .after(TransformSystem::TransformPropagate)
415                        .after(SimulationLightSystems::UpdateLightFrusta)
416                        // NOTE: This MUST be scheduled AFTER the core renderer visibility check
417                        // because that resets entity `ViewVisibility` for the first view
418                        // which would override any results from this otherwise
419                        .after(VisibilitySystems::CheckVisibility),
420                ),
421            );
422
423        if self.add_default_deferred_lighting_plugin {
424            app.add_plugins(DeferredPbrLightingPlugin);
425        }
426
427        // Initialize the default material handle.
428        app.world_mut()
429            .resource_mut::<Assets<StandardMaterial>>()
430            .insert(
431                &Handle::<StandardMaterial>::default(),
432                StandardMaterial {
433                    base_color: Color::srgb(1.0, 0.0, 0.5),
434                    ..Default::default()
435                },
436            );
437
438        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
439            return;
440        };
441
442        // Extract the required data from the main world
443        render_app
444            .add_systems(ExtractSchedule, (extract_clusters, extract_lights))
445            .add_systems(
446                Render,
447                (
448                    prepare_lights
449                        .in_set(RenderSet::ManageViews)
450                        .after(prepare_assets::<GpuImage>),
451                    prepare_clusters.in_set(RenderSet::PrepareResources),
452                ),
453            )
454            .init_resource::<LightMeta>();
455
456        render_app.world_mut().add_observer(add_light_view_entities);
457        render_app
458            .world_mut()
459            .add_observer(remove_light_view_entities);
460        render_app.world_mut().add_observer(extracted_light_removed);
461
462        let shadow_pass_node = ShadowPassNode::new(render_app.world_mut());
463        let mut graph = render_app.world_mut().resource_mut::<RenderGraph>();
464        let draw_3d_graph = graph.get_sub_graph_mut(Core3d).unwrap();
465        draw_3d_graph.add_node(NodePbr::ShadowPass, shadow_pass_node);
466        draw_3d_graph.add_node_edge(NodePbr::ShadowPass, Node3d::StartMainPass);
467    }
468
469    fn finish(&self, app: &mut App) {
470        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
471            return;
472        };
473
474        // Extract the required data from the main world
475        render_app
476            .init_resource::<ShadowSamplers>()
477            .init_resource::<GlobalClusterableObjectMeta>();
478    }
479}
480
481/// [`CameraProjection`] specific PBR functionality.
482pub struct PbrProjectionPlugin<T: CameraProjection + Component>(PhantomData<T>);
483impl<T: CameraProjection + Component> Plugin for PbrProjectionPlugin<T> {
484    fn build(&self, app: &mut App) {
485        app.add_systems(
486            PostUpdate,
487            build_directional_light_cascades::<T>
488                .in_set(SimulationLightSystems::UpdateDirectionalLightCascades)
489                .after(clear_directional_light_cascades),
490        );
491    }
492}
493impl<T: CameraProjection + Component> Default for PbrProjectionPlugin<T> {
494    fn default() -> Self {
495        Self(Default::default())
496    }
497}