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