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;
29pub mod contact_shadows;
30#[cfg(feature = "bevy_gltf")]
31mod gltf;
32use bevy_light::cluster::GlobalClusterSettings;
33use bevy_render::{
34 sync_component::SyncComponent,
35 view::{
36 RenderExtractedShadowMapVisibleEntities, RenderShadowLodOrigin,
37 RenderShadowMapVisibleEntities,
38 },
39};
40pub use contact_shadows::{
41 ContactShadows, ContactShadowsBuffer, ContactShadowsPlugin, ContactShadowsUniform,
42 ViewContactShadowsUniformOffset,
43};
44pub mod decal;
45pub mod deferred;
46pub mod diagnostic;
47mod extended_material;
48mod fog;
49mod light_probe;
50mod lightmap;
51mod material;
52mod material_bind_groups;
53mod medium;
54mod mesh_material;
55mod parallax;
56mod pbr_material;
57mod prepass;
58mod render;
59mod ssao;
60mod ssr;
61mod transmission;
62mod volumetric_fog;
63
64use bevy_color::{Color, LinearRgba};
65
66pub use atmosphere::*;
67use bevy_light::{
68 AmbientLight, DirectionalLight, PointLight, RectLight, ShadowFilteringMethod, SpotLight,
69};
70use bevy_shader::{load_shader_library, ShaderRef};
71pub use cluster::*;
72pub use decal::clustered::ClusteredDecalPlugin;
73pub use extended_material::*;
74pub use fog::*;
75pub use light_probe::*;
76pub use lightmap::*;
77pub use material::*;
78pub use material_bind_groups::*;
79pub use medium::*;
80pub use mesh_material::*;
81pub use parallax::*;
82pub use pbr_material::*;
83pub use prepass::*;
84pub use render::*;
85pub use ssao::*;
86pub use ssr::*;
87pub use transmission::*;
88pub use volumetric_fog::VolumetricFogPlugin;
89
90pub mod prelude {
94 #[doc(hidden)]
95 pub use crate::{
96 contact_shadows::ContactShadowsPlugin,
97 fog::{DistanceFog, FogFalloff},
98 material::{Material, MaterialPlugin},
99 mesh_material::MeshMaterial3d,
100 parallax::ParallaxMappingMethod,
101 pbr_material::StandardMaterial,
102 ssao::ScreenSpaceAmbientOcclusionPlugin,
103 };
104}
105
106use crate::gpu::GpuClusteringPlugin;
107use crate::{deferred::DeferredPbrLightingPlugin, gpu::extract_clusters_for_gpu_clustering};
108use bevy_app::prelude::*;
109use bevy_asset::{AssetApp, AssetPath, Assets, Handle, RenderAssetUsages};
110use bevy_core_pipeline::mip_generation::experimental::depth::early_downsample_depth;
111use bevy_core_pipeline::schedule::{Core3d, Core3dSystems};
112use bevy_ecs::prelude::*;
113use bevy_image::{Image, ImageSampler};
114use bevy_material::AlphaMode;
115use bevy_render::{
116 camera::sort_cameras,
117 extract_resource::ExtractResourcePlugin,
118 render_resource::{
119 Extent3d, TextureDataOrder, TextureDescriptor, TextureDimension, TextureFormat,
120 TextureUsages, TextureViewDescriptor, TextureViewDimension,
121 },
122 sync_component::SyncComponentPlugin,
123 ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, RenderStartup,
124 RenderSystems,
125};
126
127use std::path::PathBuf;
128
129fn shader_ref(path: PathBuf) -> ShaderRef {
130 ShaderRef::Path(AssetPath::from_path_buf(path).with_source("embedded"))
131}
132
133pub struct PbrPlugin {
135 pub prepass_enabled: bool,
138 pub add_default_deferred_lighting_plugin: bool,
140 pub use_gpu_instance_buffer_builder: bool,
145 pub debug_flags: RenderDebugFlags,
147 pub gltf_enable_standard_materials: bool,
149}
150
151impl Default for PbrPlugin {
152 fn default() -> Self {
153 Self {
154 prepass_enabled: true,
155 add_default_deferred_lighting_plugin: true,
156 use_gpu_instance_buffer_builder: true,
157 debug_flags: RenderDebugFlags::default(),
158 gltf_enable_standard_materials: true,
159 }
160 }
161}
162
163#[derive(Resource)]
165pub struct Bluenoise {
166 pub texture: Handle<Image>,
168}
169
170#[derive(Resource, Clone)]
178pub struct AreaLightLuts {
179 pub image: Handle<Image>,
180}
181
182#[derive(Resource, Clone)]
185pub struct DfgLut {
186 pub texture: Handle<Image>,
187}
188
189impl Plugin for PbrPlugin {
190 fn build(&self, app: &mut App) {
191 load_shader_library!(app, "render/pbr_types.wgsl");
192 load_shader_library!(app, "render/pbr_bindings.wgsl");
193 load_shader_library!(app, "render/utils.wgsl");
194 load_shader_library!(app, "render/clustered_forward.wgsl");
195 load_shader_library!(app, "render/pbr_lighting.wgsl");
196 load_shader_library!(app, "render/shadows.wgsl");
197 load_shader_library!(app, "deferred/pbr_deferred_types.wgsl");
198 load_shader_library!(app, "deferred/pbr_deferred_functions.wgsl");
199 load_shader_library!(app, "render/shadow_sampling.wgsl");
200 load_shader_library!(app, "render/pbr_functions.wgsl");
201 load_shader_library!(app, "render/rgb9e5.wgsl");
202 load_shader_library!(app, "render/pbr_ambient.wgsl");
203 load_shader_library!(app, "render/pbr_fragment.wgsl");
204 load_shader_library!(app, "render/pbr.wgsl");
205 load_shader_library!(app, "render/pbr_prepass_functions.wgsl");
206 load_shader_library!(app, "render/pbr_prepass.wgsl");
207 load_shader_library!(app, "render/parallax_mapping.wgsl");
208 load_shader_library!(app, "render/view_transformations.wgsl");
209
210 load_shader_library!(app, "meshlet/dummy_visibility_buffer_resolve.wgsl");
212
213 app.register_asset_reflect::<StandardMaterial>()
214 .init_resource::<DefaultOpaqueRendererMethod>()
215 .add_plugins((
216 MeshRenderPlugin {
217 use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
218 debug_flags: self.debug_flags,
219 },
220 MaterialsPlugin {
221 debug_flags: self.debug_flags,
222 },
223 MaterialPlugin::<StandardMaterial> {
224 debug_flags: self.debug_flags,
225 ..Default::default()
226 },
227 ScreenSpaceAmbientOcclusionPlugin,
228 FogPlugin,
229 ExtractResourcePlugin::<DefaultOpaqueRendererMethod>::default(),
230 SyncComponentPlugin::<ShadowFilteringMethod, Self>::default(),
231 LightmapPlugin,
232 LightProbePlugin,
233 GpuMeshPreprocessPlugin {
234 use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
235 },
236 VolumetricFogPlugin,
237 ScreenSpaceReflectionsPlugin,
238 ScreenSpaceTransmissionPlugin,
239 ClusteredDecalPlugin,
240 ContactShadowsPlugin,
241 ))
242 .add_plugins((
243 decal::ForwardDecalPlugin,
244 SyncComponentPlugin::<DirectionalLight, Self>::default(),
245 SyncComponentPlugin::<PointLight, Self>::default(),
246 SyncComponentPlugin::<SpotLight, Self>::default(),
247 SyncComponentPlugin::<RectLight, Self>::default(),
248 SyncComponentPlugin::<AmbientLight, Self>::default(),
249 ))
250 .add_plugins((
251 ScatteringMediumPlugin,
252 AtmospherePlugin,
253 GpuClusteringPlugin,
254 ));
255
256 #[cfg(feature = "bevy_gltf")]
257 if self.gltf_enable_standard_materials {
258 gltf::add_gltf(app);
259 }
260
261 if self.add_default_deferred_lighting_plugin {
262 app.add_plugins(DeferredPbrLightingPlugin);
263 }
264
265 app.world_mut()
267 .resource_mut::<Assets<StandardMaterial>>()
268 .insert(
269 &Handle::<StandardMaterial>::default(),
270 StandardMaterial {
271 base_color: Color::srgb(1.0, 0.0, 0.5),
272 ..Default::default()
273 },
274 )
275 .unwrap();
276
277 let has_bluenoise = app
278 .get_sub_app(RenderApp)
279 .is_some_and(|render_app| render_app.world().is_resource_added::<Bluenoise>());
280
281 if !has_bluenoise {
282 let mut images = app.world_mut().resource_mut::<Assets<Image>>();
283 #[cfg(feature = "bluenoise_texture")]
284 let handle = {
285 let mut image = Image::from_buffer(
286 include_bytes!("bluenoise/stbn.ktx2"),
287 bevy_image::ImageType::Extension("ktx2"),
288 bevy_image::CompressedImageFormats::NONE,
289 false,
290 ImageSampler::Default,
291 RenderAssetUsages::RENDER_WORLD,
292 )
293 .expect("Failed to decode embedded blue-noise texture");
294 image.texture_descriptor.label = Some("bluenoise");
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 has_area_light_luts = app
309 .get_sub_app(RenderApp)
310 .is_some_and(|render_app| render_app.world().is_resource_added::<AreaLightLuts>());
311
312 if !has_area_light_luts {
313 let mut images = app.world_mut().resource_mut::<Assets<Image>>();
314 #[cfg(feature = "area_light_luts")]
315 let handle = {
316 let mut image = Image::from_buffer(
317 include_bytes!("ltc/ltc.ktx2"),
318 bevy_image::ImageType::Extension("ktx2"),
319 bevy_image::CompressedImageFormats::NONE,
320 false,
321 ImageSampler::linear(),
322 RenderAssetUsages::RENDER_WORLD,
323 )
324 .expect("Failed to decode embedded LTC LUTs");
325 image.texture_descriptor.label = Some("area_light_luts");
326 images.add(image)
327 };
328 #[cfg(not(feature = "area_light_luts"))]
329 let handle = images.add(area_light_luts_placeholder());
330
331 let area_light_luts = AreaLightLuts { image: handle };
332 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
333 render_app.world_mut().insert_resource(area_light_luts);
334 }
335 }
336
337 let has_dfg_lut = app
338 .get_sub_app(RenderApp)
339 .is_some_and(|render_app| render_app.world().is_resource_added::<DfgLut>());
340
341 if !has_dfg_lut {
342 #[cfg(feature = "dfg_lut")]
343 let texture = app.world_mut().resource_mut::<Assets<Image>>().add(
344 Image::from_buffer(
345 include_bytes!("environment_map/dfg.ktx2"),
346 bevy_image::ImageType::Extension("ktx2"),
347 bevy_image::CompressedImageFormats::NONE,
348 false,
349 ImageSampler::linear(),
350 RenderAssetUsages::RENDER_WORLD,
351 )
352 .expect("Failed to decode embedded DFG LUT"),
353 );
354 #[cfg(not(feature = "dfg_lut"))]
355 let texture = Handle::default();
356
357 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
358 render_app.world_mut().insert_resource(DfgLut { texture });
359 }
360 }
361
362 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
363 return;
364 };
365
366 render_app
368 .add_systems(
369 RenderStartup,
370 (
371 init_shadow_samplers,
372 init_global_clusterable_object_meta,
373 init_fallback_bindless_resources,
374 ),
375 )
376 .add_systems(
377 ExtractSchedule,
378 (
379 extract_clusters_for_cpu_clustering
380 .run_if(not(gpu_clustering_is_enabled_during_extraction)),
381 extract_clusters_for_gpu_clustering
382 .run_if(gpu_clustering_is_enabled_during_extraction),
383 ),
384 )
385 .add_systems(
386 ExtractSchedule,
387 (
388 extract_lights,
389 extract_ambient_light_resource,
390 extract_ambient_light,
391 extract_shadow_filtering_method,
392 extract_shadow_lod_origin,
393 late_sweep_material_instances,
394 ),
395 )
396 .add_systems(
397 Render,
398 (
399 prepare_lights
400 .in_set(RenderSystems::CreateViews)
401 .after(sort_cameras),
402 prepare_clusters_for_cpu_clustering
403 .in_set(RenderSystems::PrepareResources)
404 .run_if(
405 |global_cluster_settings: Res<GlobalClusterSettings>| -> bool {
406 global_cluster_settings.gpu_clustering.is_none()
407 },
408 ),
409 ),
410 )
411 .init_gpu_resource::<LightMeta>()
412 .init_gpu_resource::<RenderMaterialBindings>()
413 .init_resource::<RenderShadowLodOrigin>()
414 .allow_ambiguous_resource::<RenderMaterialBindings>();
415
416 render_app.world_mut().add_observer(add_light_view_entities);
417 render_app
418 .world_mut()
419 .add_observer(remove_light_view_entities);
420 render_app
421 .world_mut()
422 .add_observer(remove_point_and_spot_light_view_entities);
423
424 render_app.add_systems(
425 Core3d,
426 (
427 per_view_shadow_pass::<EARLY_SHADOW_PASS>
428 .after(early_prepass_build_indirect_parameters)
429 .before(early_downsample_depth)
430 .before(per_view_shadow_pass::<LATE_SHADOW_PASS>),
431 per_view_shadow_pass::<LATE_SHADOW_PASS>
432 .after(late_prepass_build_indirect_parameters)
433 .before(main_build_indirect_parameters)
434 .before(Core3dSystems::MainPass),
435 shared_shadow_pass::<EARLY_SHADOW_PASS>
436 .after(early_prepass_build_indirect_parameters)
437 .before(early_downsample_depth)
438 .before(shared_shadow_pass::<LATE_SHADOW_PASS>),
439 shared_shadow_pass::<LATE_SHADOW_PASS>
440 .after(late_prepass_build_indirect_parameters)
441 .before(main_build_indirect_parameters)
442 .before(Core3dSystems::MainPass),
443 ),
444 );
445 }
446
447 fn finish(&self, app: &mut App) {
448 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
449 return;
450 };
451
452 let global_cluster_settings = make_global_cluster_settings(render_app.world());
453 app.insert_resource(global_cluster_settings);
454 }
455}
456
457pub fn stbn_placeholder() -> Image {
458 let format = TextureFormat::Rgba8Unorm;
459 let data = vec![255, 0, 255, 255];
460 Image {
461 data: Some(data),
462 data_order: TextureDataOrder::default(),
463 texture_descriptor: TextureDescriptor {
464 size: Extent3d::default(),
465 format,
466 dimension: TextureDimension::D2,
467 label: Some("bluenoise_placeholder"),
468 mip_level_count: 1,
469 sample_count: 1,
470 usage: TextureUsages::TEXTURE_BINDING,
471 view_formats: &[],
472 },
473 sampler: ImageSampler::Default,
474 texture_view_descriptor: None,
475 asset_usage: RenderAssetUsages::RENDER_WORLD,
476 copy_on_resize: false,
477 }
478}
479
480pub fn area_light_luts_placeholder() -> Image {
481 let format = TextureFormat::Rgba16Float;
482 let data = vec![0; 16];
483 Image {
484 data: Some(data),
485 data_order: TextureDataOrder::default(),
486 texture_descriptor: TextureDescriptor {
487 size: Extent3d {
488 width: 1,
489 height: 1,
490 depth_or_array_layers: 2,
491 },
492 format,
493 dimension: TextureDimension::D2,
494 label: Some("area_light_luts_placeholder"),
495 mip_level_count: 1,
496 sample_count: 1,
497 usage: TextureUsages::TEXTURE_BINDING,
498 view_formats: &[],
499 },
500 sampler: ImageSampler::Default,
501 texture_view_descriptor: Some(TextureViewDescriptor {
502 dimension: Some(TextureViewDimension::D2Array),
503 ..Default::default()
504 }),
505 asset_usage: RenderAssetUsages::RENDER_WORLD,
506 copy_on_resize: false,
507 }
508}
509
510impl SyncComponent<PbrPlugin> for DirectionalLight {
511 type Target = (
512 Self,
513 ExtractedDirectionalLight,
514 RenderExtractedShadowMapVisibleEntities,
515 RenderShadowMapVisibleEntities,
516 DirectionalLightViewEntities,
517 );
518}
519impl SyncComponent<PbrPlugin> for PointLight {
520 type Target = (
521 Self,
522 ExtractedPointLight,
523 RenderExtractedShadowMapVisibleEntities,
524 RenderShadowMapVisibleEntities,
525 PointAndSpotLightViewEntities,
526 );
527}
528impl SyncComponent<PbrPlugin> for SpotLight {
529 type Target = (
530 Self,
531 ExtractedPointLight,
532 RenderExtractedShadowMapVisibleEntities,
533 RenderShadowMapVisibleEntities,
534 PointAndSpotLightViewEntities,
535 );
536}
537impl SyncComponent<PbrPlugin> for RectLight {
538 type Target = (Self, ExtractedRectLight);
539}
540impl SyncComponent<PbrPlugin> for AmbientLight {
541 type Target = Self;
542}
543impl SyncComponent<PbrPlugin> for ShadowFilteringMethod {
544 type Target = Self;
545}