#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
extern crate alloc;
#[cfg(feature = "meshlet")]
mod meshlet;
pub mod wireframe;
#[cfg(feature = "meshlet")]
pub mod experimental {
pub mod meshlet {
pub use crate::meshlet::*;
}
}
mod atmosphere;
mod cluster;
mod components;
pub mod contact_shadows;
use bevy_gltf::{
extensions::{GltfExtensionHandler, GltfExtensionHandlers},
GltfAssetLabel,
};
use bevy_render::sync_component::SyncComponent;
pub use contact_shadows::{
ContactShadows, ContactShadowsBuffer, ContactShadowsPlugin, ContactShadowsUniform,
ViewContactShadowsUniformOffset,
};
pub mod decal;
pub mod deferred;
pub mod diagnostic;
mod extended_material;
mod fog;
mod light_probe;
mod lightmap;
mod material;
mod material_bind_groups;
mod medium;
mod mesh_material;
mod parallax;
mod pbr_material;
mod prepass;
mod render;
mod ssao;
mod ssr;
mod transmission;
mod volumetric_fog;
use bevy_color::{Color, LinearRgba};
pub use atmosphere::*;
use bevy_asset::LoadContext;
use bevy_gltf::{gltf, GltfMaterial};
use bevy_light::{AmbientLight, DirectionalLight, PointLight, ShadowFilteringMethod, SpotLight};
use bevy_shader::{load_shader_library, ShaderRef};
pub use cluster::*;
pub use components::*;
pub use decal::clustered::ClusteredDecalPlugin;
pub use extended_material::*;
pub use fog::*;
pub use light_probe::*;
pub use lightmap::*;
pub use material::*;
pub use material_bind_groups::*;
pub use medium::*;
pub use mesh_material::*;
pub use parallax::*;
pub use pbr_material::*;
pub use prepass::*;
pub use render::*;
pub use ssao::*;
pub use ssr::*;
pub use transmission::*;
pub use volumetric_fog::VolumetricFogPlugin;
pub mod prelude {
#[doc(hidden)]
pub use crate::{
contact_shadows::ContactShadowsPlugin,
fog::{DistanceFog, FogFalloff},
material::{Material, MaterialPlugin},
mesh_material::MeshMaterial3d,
parallax::ParallaxMappingMethod,
pbr_material::StandardMaterial,
ssao::ScreenSpaceAmbientOcclusionPlugin,
};
}
use crate::deferred::DeferredPbrLightingPlugin;
use bevy_app::prelude::*;
use bevy_asset::{AssetApp, AssetPath, Assets, Handle, RenderAssetUsages};
use bevy_core_pipeline::mip_generation::experimental::depth::early_downsample_depth;
use bevy_core_pipeline::schedule::{Core3d, Core3dSystems};
use bevy_ecs::prelude::*;
#[cfg(feature = "bluenoise_texture")]
use bevy_image::{CompressedImageFormats, ImageType};
use bevy_image::{Image, ImageSampler};
use bevy_material::AlphaMode;
use bevy_render::{
camera::sort_cameras,
extract_resource::ExtractResourcePlugin,
render_resource::{
Extent3d, TextureDataOrder, TextureDescriptor, TextureDimension, TextureFormat,
TextureUsages,
},
sync_component::SyncComponentPlugin,
ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderStartup, RenderSystems,
};
use std::path::PathBuf;
fn shader_ref(path: PathBuf) -> ShaderRef {
ShaderRef::Path(AssetPath::from_path_buf(path).with_source("embedded"))
}
pub const TONEMAPPING_LUT_TEXTURE_BINDING_INDEX: u32 = 19;
pub const TONEMAPPING_LUT_SAMPLER_BINDING_INDEX: u32 = 20;
pub struct PbrPlugin {
pub prepass_enabled: bool,
pub add_default_deferred_lighting_plugin: bool,
pub use_gpu_instance_buffer_builder: bool,
pub debug_flags: RenderDebugFlags,
pub gltf_render_enabled: bool,
}
impl Default for PbrPlugin {
fn default() -> Self {
Self {
prepass_enabled: true,
add_default_deferred_lighting_plugin: true,
use_gpu_instance_buffer_builder: true,
debug_flags: RenderDebugFlags::default(),
gltf_render_enabled: true,
}
}
}
#[derive(Resource)]
pub struct Bluenoise {
pub texture: Handle<Image>,
}
impl Plugin for PbrPlugin {
fn build(&self, app: &mut App) {
load_shader_library!(app, "render/pbr_types.wgsl");
load_shader_library!(app, "render/pbr_bindings.wgsl");
load_shader_library!(app, "render/utils.wgsl");
load_shader_library!(app, "render/clustered_forward.wgsl");
load_shader_library!(app, "render/pbr_lighting.wgsl");
load_shader_library!(app, "render/shadows.wgsl");
load_shader_library!(app, "deferred/pbr_deferred_types.wgsl");
load_shader_library!(app, "deferred/pbr_deferred_functions.wgsl");
load_shader_library!(app, "render/shadow_sampling.wgsl");
load_shader_library!(app, "render/pbr_functions.wgsl");
load_shader_library!(app, "render/rgb9e5.wgsl");
load_shader_library!(app, "render/pbr_ambient.wgsl");
load_shader_library!(app, "render/pbr_fragment.wgsl");
load_shader_library!(app, "render/pbr.wgsl");
load_shader_library!(app, "render/pbr_prepass_functions.wgsl");
load_shader_library!(app, "render/pbr_prepass.wgsl");
load_shader_library!(app, "render/parallax_mapping.wgsl");
load_shader_library!(app, "render/view_transformations.wgsl");
load_shader_library!(app, "meshlet/dummy_visibility_buffer_resolve.wgsl");
app.register_asset_reflect::<StandardMaterial>()
.init_resource::<DefaultOpaqueRendererMethod>()
.add_plugins((
MeshRenderPlugin {
use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
debug_flags: self.debug_flags,
},
MaterialsPlugin {
debug_flags: self.debug_flags,
},
MaterialPlugin::<StandardMaterial> {
debug_flags: self.debug_flags,
..Default::default()
},
ScreenSpaceAmbientOcclusionPlugin,
FogPlugin,
ExtractResourcePlugin::<DefaultOpaqueRendererMethod>::default(),
SyncComponentPlugin::<ShadowFilteringMethod, Self>::default(),
LightmapPlugin,
LightProbePlugin,
GpuMeshPreprocessPlugin {
use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
},
VolumetricFogPlugin,
ScreenSpaceReflectionsPlugin,
ScreenSpaceTransmissionPlugin,
ClusteredDecalPlugin,
ContactShadowsPlugin,
))
.add_plugins((
decal::ForwardDecalPlugin,
SyncComponentPlugin::<DirectionalLight, Self>::default(),
SyncComponentPlugin::<PointLight, Self>::default(),
SyncComponentPlugin::<SpotLight, Self>::default(),
SyncComponentPlugin::<AmbientLight, Self>::default(),
))
.add_plugins((ScatteringMediumPlugin, AtmospherePlugin));
if self.gltf_render_enabled {
#[cfg(target_family = "wasm")]
bevy_tasks::block_on(async {
app.world_mut()
.resource_mut::<GltfExtensionHandlers>()
.0
.write()
.await
.push(Box::new(GltfExtensionHandlerPbr))
});
#[cfg(not(target_family = "wasm"))]
app.world_mut()
.resource_mut::<GltfExtensionHandlers>()
.0
.write_blocking()
.push(Box::new(GltfExtensionHandlerPbr));
}
if self.add_default_deferred_lighting_plugin {
app.add_plugins(DeferredPbrLightingPlugin);
}
app.world_mut()
.resource_mut::<Assets<StandardMaterial>>()
.insert(
&Handle::<StandardMaterial>::default(),
StandardMaterial {
base_color: Color::srgb(1.0, 0.0, 0.5),
..Default::default()
},
)
.unwrap();
let has_bluenoise = app
.get_sub_app(RenderApp)
.is_some_and(|render_app| render_app.world().is_resource_added::<Bluenoise>());
if !has_bluenoise {
let mut images = app.world_mut().resource_mut::<Assets<Image>>();
#[cfg(feature = "bluenoise_texture")]
let handle = {
let image = Image::from_buffer(
include_bytes!("bluenoise/stbn.ktx2"),
ImageType::Extension("ktx2"),
CompressedImageFormats::NONE,
false,
ImageSampler::Default,
RenderAssetUsages::RENDER_WORLD,
)
.expect("Failed to decode embedded blue-noise texture");
images.add(image)
};
#[cfg(not(feature = "bluenoise_texture"))]
let handle = { images.add(stbn_placeholder()) };
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.world_mut()
.insert_resource(Bluenoise { texture: handle });
}
}
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.add_systems(
RenderStartup,
(
init_shadow_samplers,
init_global_clusterable_object_meta,
init_fallback_bindless_resources,
),
)
.add_systems(
ExtractSchedule,
(
extract_clusters,
extract_lights,
extract_ambient_light_resource,
extract_ambient_light,
extract_shadow_filtering_method,
late_sweep_material_instances,
),
)
.add_systems(
Render,
(
prepare_lights
.in_set(RenderSystems::ManageViews)
.after(sort_cameras),
prepare_clusters.in_set(RenderSystems::PrepareResources),
),
)
.init_resource::<LightMeta>()
.init_resource::<RenderMaterialBindings>();
render_app.world_mut().add_observer(add_light_view_entities);
render_app
.world_mut()
.add_observer(remove_light_view_entities);
render_app.world_mut().add_observer(extracted_light_removed);
render_app.add_systems(
Core3d,
(
shadow_pass::<EARLY_SHADOW_PASS>
.after(early_prepass_build_indirect_parameters)
.before(early_downsample_depth)
.before(shadow_pass::<LATE_SHADOW_PASS>),
shadow_pass::<LATE_SHADOW_PASS>
.after(late_prepass_build_indirect_parameters)
.before(main_build_indirect_parameters)
.before(Core3dSystems::MainPass),
),
);
}
fn finish(&self, app: &mut App) {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
let global_cluster_settings = make_global_cluster_settings(render_app.world());
app.insert_resource(global_cluster_settings);
}
}
pub fn stbn_placeholder() -> Image {
let format = TextureFormat::Rgba8Unorm;
let data = vec![255, 0, 255, 255];
Image {
data: Some(data),
data_order: TextureDataOrder::default(),
texture_descriptor: TextureDescriptor {
size: Extent3d::default(),
format,
dimension: TextureDimension::D2,
label: None,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::TEXTURE_BINDING,
view_formats: &[],
},
sampler: ImageSampler::Default,
texture_view_descriptor: None,
asset_usage: RenderAssetUsages::RENDER_WORLD,
copy_on_resize: false,
}
}
fn standard_material_from_gltf_material(material: &GltfMaterial) -> StandardMaterial {
StandardMaterial {
base_color: material.base_color,
base_color_channel: material.base_color_channel.clone(),
base_color_texture: material.base_color_texture.clone(),
emissive: material.emissive,
emissive_channel: material.emissive_channel.clone(),
emissive_texture: material.emissive_texture.clone(),
perceptual_roughness: material.perceptual_roughness,
metallic: material.metallic,
metallic_roughness_channel: material.metallic_roughness_channel.clone(),
metallic_roughness_texture: material.metallic_roughness_texture.clone(),
reflectance: material.reflectance,
specular_tint: material.specular_tint,
specular_transmission: material.specular_transmission,
#[cfg(feature = "pbr_transmission_textures")]
specular_transmission_channel: material.specular_transmission_channel.clone(),
#[cfg(feature = "pbr_transmission_textures")]
specular_transmission_texture: material.specular_transmission_texture.clone(),
thickness: material.thickness,
#[cfg(feature = "pbr_transmission_textures")]
thickness_channel: material.thickness_channel.clone(),
#[cfg(feature = "pbr_transmission_textures")]
thickness_texture: material.thickness_texture.clone(),
ior: material.ior,
attenuation_distance: material.attenuation_distance,
attenuation_color: material.attenuation_color,
normal_map_channel: material.normal_map_channel.clone(),
normal_map_texture: material.normal_map_texture.clone(),
occlusion_channel: material.occlusion_channel.clone(),
occlusion_texture: material.occlusion_texture.clone(),
#[cfg(feature = "pbr_specular_textures")]
specular_channel: material.specular_channel.clone(),
#[cfg(feature = "pbr_specular_textures")]
specular_texture: material.specular_texture.clone(),
#[cfg(feature = "pbr_specular_textures")]
specular_tint_channel: material.specular_tint_channel.clone(),
#[cfg(feature = "pbr_specular_textures")]
specular_tint_texture: material.specular_tint_texture.clone(),
clearcoat: material.clearcoat,
clearcoat_perceptual_roughness: material.clearcoat_perceptual_roughness,
#[cfg(feature = "pbr_multi_layer_material_textures")]
clearcoat_roughness_channel: material.clearcoat_roughness_channel.clone(),
#[cfg(feature = "pbr_multi_layer_material_textures")]
clearcoat_roughness_texture: material.clearcoat_roughness_texture.clone(),
#[cfg(feature = "pbr_multi_layer_material_textures")]
clearcoat_normal_channel: material.clearcoat_normal_channel.clone(),
#[cfg(feature = "pbr_multi_layer_material_textures")]
clearcoat_normal_texture: material.clearcoat_normal_texture.clone(),
anisotropy_strength: material.anisotropy_strength,
anisotropy_rotation: material.anisotropy_rotation,
#[cfg(feature = "pbr_anisotropy_texture")]
anisotropy_channel: material.anisotropy_channel.clone(),
#[cfg(feature = "pbr_anisotropy_texture")]
anisotropy_texture: material.anisotropy_texture.clone(),
double_sided: material.double_sided,
cull_mode: material.cull_mode,
unlit: material.unlit,
alpha_mode: material.alpha_mode,
uv_transform: material.uv_transform,
..Default::default()
}
}
#[derive(Default, Clone)]
struct GltfExtensionHandlerPbr;
impl GltfExtensionHandler for GltfExtensionHandlerPbr {
fn dyn_clone(&self) -> Box<dyn GltfExtensionHandler> {
Box::new((*self).clone())
}
fn on_root(&mut self, load_context: &mut LoadContext<'_>, _gltf: &gltf::Gltf) {
let std_label = format!("{}#std", GltfAssetLabel::DefaultMaterial);
load_context.add_labeled_asset(
std_label,
standard_material_from_gltf_material(&GltfMaterial::default()),
);
}
fn on_material(
&mut self,
load_context: &mut LoadContext<'_>,
_gltf_material: &gltf::Material,
_material: Handle<GltfMaterial>,
material_asset: &GltfMaterial,
material_label: &str,
) {
let std_label = format!("{}#std", material_label);
load_context.add_labeled_asset(
std_label,
standard_material_from_gltf_material(material_asset),
);
}
fn on_spawn_mesh_and_material(
&mut self,
load_context: &mut LoadContext<'_>,
_primitive: &gltf::Primitive,
_mesh: &gltf::Mesh,
_material: &gltf::Material,
entity: &mut EntityWorldMut,
material_label: &str,
) {
let std_label = format!("{}#std", material_label);
let handle = load_context.get_label_handle::<StandardMaterial>(std_label);
entity.insert(MeshMaterial3d(handle));
}
}
impl SyncComponent<PbrPlugin> for DirectionalLight {
type Out = Self;
}
impl SyncComponent<PbrPlugin> for PointLight {
type Out = Self;
}
impl SyncComponent<PbrPlugin> for SpotLight {
type Out = Self;
}
impl SyncComponent<PbrPlugin> for AmbientLight {
type Out = Self;
}
impl SyncComponent<PbrPlugin> for ShadowFilteringMethod {
type Out = Self;
}