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#[cfg(feature = "meshlet")]
19pub mod experimental {
20 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
73pub 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 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
93 pub enum NodePbr {
94 EarlyShadowPass,
97 LateShadowPass,
100 ScreenSpaceAmbientOcclusion,
102 DeferredLightingPass,
103 VolumetricFog,
105 EarlyGpuPreprocess,
108 LateGpuPreprocess,
111 ScreenSpaceReflections,
113 EarlyPrepassBuildIndirectParameters,
116 LatePrepassBuildIndirectParameters,
119 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
156pub struct PbrPlugin {
158 pub prepass_enabled: bool,
161 pub add_default_deferred_lighting_plugin: bool,
163 pub use_gpu_instance_buffer_builder: bool,
168 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#[derive(Resource)]
185pub struct Bluenoise {
186 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 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 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 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}