bevy_pbr/
material.rs

1use self::{irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight};
2#[cfg(feature = "meshlet")]
3use crate::meshlet::{
4    prepare_material_meshlet_meshes_main_opaque_pass, queue_material_meshlet_meshes,
5    InstanceManager,
6};
7use crate::*;
8use bevy_asset::{Asset, AssetId, AssetServer};
9use bevy_core_pipeline::{
10    core_3d::{
11        AlphaMask3d, Camera3d, Opaque3d, Opaque3dBinKey, ScreenSpaceTransmissionQuality,
12        Transmissive3d, Transparent3d,
13    },
14    oit::OrderIndependentTransparencySettings,
15    prepass::{
16        DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, OpaqueNoLightmap3dBinKey,
17    },
18    tonemapping::{DebandDither, Tonemapping},
19};
20use bevy_derive::{Deref, DerefMut};
21use bevy_ecs::{
22    prelude::*,
23    system::{lifetimeless::SRes, SystemParamItem},
24};
25use bevy_reflect::std_traits::ReflectDefault;
26use bevy_reflect::Reflect;
27use bevy_render::sync_world::MainEntityHashMap;
28use bevy_render::view::RenderVisibleEntities;
29use bevy_render::{
30    camera::TemporalJitter,
31    extract_resource::ExtractResource,
32    mesh::{Mesh3d, MeshVertexBufferLayoutRef, RenderMesh},
33    render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
34    render_phase::*,
35    render_resource::*,
36    renderer::RenderDevice,
37    view::{ExtractedView, Msaa, RenderVisibilityRanges, ViewVisibility},
38    Extract,
39};
40use bevy_utils::tracing::error;
41use core::{
42    hash::Hash,
43    marker::PhantomData,
44    num::NonZero,
45    sync::atomic::{AtomicU32, Ordering},
46};
47
48/// Materials are used alongside [`MaterialPlugin`], [`Mesh3d`], and [`MeshMaterial3d`]
49/// to spawn entities that are rendered with a specific [`Material`] type. They serve as an easy to use high level
50/// way to render [`Mesh3d`] entities with custom shader logic.
51///
52/// Materials must implement [`AsBindGroup`] to define how data will be transferred to the GPU and bound in shaders.
53/// [`AsBindGroup`] can be derived, which makes generating bindings straightforward. See the [`AsBindGroup`] docs for details.
54///
55/// # Example
56///
57/// Here is a simple [`Material`] implementation. The [`AsBindGroup`] derive has many features. To see what else is available,
58/// check out the [`AsBindGroup`] documentation.
59///
60/// ```
61/// # use bevy_pbr::{Material, MeshMaterial3d};
62/// # use bevy_ecs::prelude::*;
63/// # use bevy_image::Image;
64/// # use bevy_reflect::TypePath;
65/// # use bevy_render::{mesh::{Mesh, Mesh3d}, render_resource::{AsBindGroup, ShaderRef}};
66/// # use bevy_color::LinearRgba;
67/// # use bevy_color::palettes::basic::RED;
68/// # use bevy_asset::{Handle, AssetServer, Assets, Asset};
69/// # use bevy_math::primitives::Capsule3d;
70/// #
71/// #[derive(AsBindGroup, Debug, Clone, Asset, TypePath)]
72/// pub struct CustomMaterial {
73///     // Uniform bindings must implement `ShaderType`, which will be used to convert the value to
74///     // its shader-compatible equivalent. Most core math types already implement `ShaderType`.
75///     #[uniform(0)]
76///     color: LinearRgba,
77///     // Images can be bound as textures in shaders. If the Image's sampler is also needed, just
78///     // add the sampler attribute with a different binding index.
79///     #[texture(1)]
80///     #[sampler(2)]
81///     color_texture: Handle<Image>,
82/// }
83///
84/// // All functions on `Material` have default impls. You only need to implement the
85/// // functions that are relevant for your material.
86/// impl Material for CustomMaterial {
87///     fn fragment_shader() -> ShaderRef {
88///         "shaders/custom_material.wgsl".into()
89///     }
90/// }
91///
92/// // Spawn an entity with a mesh using `CustomMaterial`.
93/// fn setup(
94///     mut commands: Commands,
95///     mut meshes: ResMut<Assets<Mesh>>,
96///     mut materials: ResMut<Assets<CustomMaterial>>,
97///     asset_server: Res<AssetServer>
98/// ) {
99///     commands.spawn((
100///         Mesh3d(meshes.add(Capsule3d::default())),
101///         MeshMaterial3d(materials.add(CustomMaterial {
102///             color: RED.into(),
103///             color_texture: asset_server.load("some_image.png"),
104///         })),
105///     ));
106/// }
107/// ```
108///
109/// In WGSL shaders, the material's binding would look like this:
110///
111/// ```wgsl
112/// @group(2) @binding(0) var<uniform> color: vec4<f32>;
113/// @group(2) @binding(1) var color_texture: texture_2d<f32>;
114/// @group(2) @binding(2) var color_sampler: sampler;
115/// ```
116pub trait Material: Asset + AsBindGroup + Clone + Sized {
117    /// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader
118    /// will be used.
119    fn vertex_shader() -> ShaderRef {
120        ShaderRef::Default
121    }
122
123    /// Returns this material's fragment shader. If [`ShaderRef::Default`] is returned, the default mesh fragment shader
124    /// will be used.
125    #[allow(unused_variables)]
126    fn fragment_shader() -> ShaderRef {
127        ShaderRef::Default
128    }
129
130    /// Returns this material's [`AlphaMode`]. Defaults to [`AlphaMode::Opaque`].
131    #[inline]
132    fn alpha_mode(&self) -> AlphaMode {
133        AlphaMode::Opaque
134    }
135
136    /// Returns if this material should be rendered by the deferred or forward renderer.
137    /// for `AlphaMode::Opaque` or `AlphaMode::Mask` materials.
138    /// If `OpaqueRendererMethod::Auto`, it will default to what is selected in the `DefaultOpaqueRendererMethod` resource.
139    #[inline]
140    fn opaque_render_method(&self) -> OpaqueRendererMethod {
141        OpaqueRendererMethod::Forward
142    }
143
144    #[inline]
145    /// Add a bias to the view depth of the mesh which can be used to force a specific render order.
146    /// for meshes with similar depth, to avoid z-fighting.
147    /// The bias is in depth-texture units so large values may be needed to overcome small depth differences.
148    fn depth_bias(&self) -> f32 {
149        0.0
150    }
151
152    #[inline]
153    /// Returns whether the material would like to read from [`ViewTransmissionTexture`](bevy_core_pipeline::core_3d::ViewTransmissionTexture).
154    ///
155    /// This allows taking color output from the [`Opaque3d`] pass as an input, (for screen-space transmission) but requires
156    /// rendering to take place in a separate [`Transmissive3d`] pass.
157    fn reads_view_transmission_texture(&self) -> bool {
158        false
159    }
160
161    /// Returns this material's prepass vertex shader. If [`ShaderRef::Default`] is returned, the default prepass vertex shader
162    /// will be used.
163    ///
164    /// This is used for the various [prepasses](bevy_core_pipeline::prepass) as well as for generating the depth maps
165    /// required for shadow mapping.
166    fn prepass_vertex_shader() -> ShaderRef {
167        ShaderRef::Default
168    }
169
170    /// Returns this material's prepass fragment shader. If [`ShaderRef::Default`] is returned, the default prepass fragment shader
171    /// will be used.
172    ///
173    /// This is used for the various [prepasses](bevy_core_pipeline::prepass) as well as for generating the depth maps
174    /// required for shadow mapping.
175    #[allow(unused_variables)]
176    fn prepass_fragment_shader() -> ShaderRef {
177        ShaderRef::Default
178    }
179
180    /// Returns this material's deferred vertex shader. If [`ShaderRef::Default`] is returned, the default deferred vertex shader
181    /// will be used.
182    fn deferred_vertex_shader() -> ShaderRef {
183        ShaderRef::Default
184    }
185
186    /// Returns this material's deferred fragment shader. If [`ShaderRef::Default`] is returned, the default deferred fragment shader
187    /// will be used.
188    #[allow(unused_variables)]
189    fn deferred_fragment_shader() -> ShaderRef {
190        ShaderRef::Default
191    }
192
193    /// Returns this material's [`crate::meshlet::MeshletMesh`] fragment shader. If [`ShaderRef::Default`] is returned,
194    /// the default meshlet mesh fragment shader will be used.
195    ///
196    /// This is part of an experimental feature, and is unnecessary to implement unless you are using `MeshletMesh`'s.
197    ///
198    /// See [`crate::meshlet::MeshletMesh`] for limitations.
199    #[allow(unused_variables)]
200    #[cfg(feature = "meshlet")]
201    fn meshlet_mesh_fragment_shader() -> ShaderRef {
202        ShaderRef::Default
203    }
204
205    /// Returns this material's [`crate::meshlet::MeshletMesh`] prepass fragment shader. If [`ShaderRef::Default`] is returned,
206    /// the default meshlet mesh prepass fragment shader will be used.
207    ///
208    /// This is part of an experimental feature, and is unnecessary to implement unless you are using `MeshletMesh`'s.
209    ///
210    /// See [`crate::meshlet::MeshletMesh`] for limitations.
211    #[allow(unused_variables)]
212    #[cfg(feature = "meshlet")]
213    fn meshlet_mesh_prepass_fragment_shader() -> ShaderRef {
214        ShaderRef::Default
215    }
216
217    /// Returns this material's [`crate::meshlet::MeshletMesh`] deferred fragment shader. If [`ShaderRef::Default`] is returned,
218    /// the default meshlet mesh deferred fragment shader will be used.
219    ///
220    /// This is part of an experimental feature, and is unnecessary to implement unless you are using `MeshletMesh`'s.
221    ///
222    /// See [`crate::meshlet::MeshletMesh`] for limitations.
223    #[allow(unused_variables)]
224    #[cfg(feature = "meshlet")]
225    fn meshlet_mesh_deferred_fragment_shader() -> ShaderRef {
226        ShaderRef::Default
227    }
228
229    /// Customizes the default [`RenderPipelineDescriptor`] for a specific entity using the entity's
230    /// [`MaterialPipelineKey`] and [`MeshVertexBufferLayoutRef`] as input.
231    #[allow(unused_variables)]
232    #[inline]
233    fn specialize(
234        pipeline: &MaterialPipeline<Self>,
235        descriptor: &mut RenderPipelineDescriptor,
236        layout: &MeshVertexBufferLayoutRef,
237        key: MaterialPipelineKey<Self>,
238    ) -> Result<(), SpecializedMeshPipelineError> {
239        Ok(())
240    }
241}
242
243/// Adds the necessary ECS resources and render logic to enable rendering entities using the given [`Material`]
244/// asset type.
245pub struct MaterialPlugin<M: Material> {
246    /// Controls if the prepass is enabled for the Material.
247    /// For more information about what a prepass is, see the [`bevy_core_pipeline::prepass`] docs.
248    ///
249    /// When it is enabled, it will automatically add the [`PrepassPlugin`]
250    /// required to make the prepass work on this Material.
251    pub prepass_enabled: bool,
252    /// Controls if shadows are enabled for the Material.
253    pub shadows_enabled: bool,
254    pub _marker: PhantomData<M>,
255}
256
257impl<M: Material> Default for MaterialPlugin<M> {
258    fn default() -> Self {
259        Self {
260            prepass_enabled: true,
261            shadows_enabled: true,
262            _marker: Default::default(),
263        }
264    }
265}
266
267impl<M: Material> Plugin for MaterialPlugin<M>
268where
269    M::Data: PartialEq + Eq + Hash + Clone,
270{
271    fn build(&self, app: &mut App) {
272        app.init_asset::<M>()
273            .register_type::<MeshMaterial3d<M>>()
274            .add_plugins(RenderAssetPlugin::<PreparedMaterial<M>>::default());
275
276        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
277            render_app
278                .init_resource::<DrawFunctions<Shadow>>()
279                .init_resource::<RenderMaterialInstances<M>>()
280                .add_render_command::<Shadow, DrawPrepass<M>>()
281                .add_render_command::<Transmissive3d, DrawMaterial<M>>()
282                .add_render_command::<Transparent3d, DrawMaterial<M>>()
283                .add_render_command::<Opaque3d, DrawMaterial<M>>()
284                .add_render_command::<AlphaMask3d, DrawMaterial<M>>()
285                .init_resource::<SpecializedMeshPipelines<MaterialPipeline<M>>>()
286                .add_systems(ExtractSchedule, extract_mesh_materials::<M>)
287                .add_systems(
288                    Render,
289                    queue_material_meshes::<M>
290                        .in_set(RenderSet::QueueMeshes)
291                        .after(prepare_assets::<PreparedMaterial<M>>),
292                );
293
294            if self.shadows_enabled {
295                render_app.add_systems(
296                    Render,
297                    queue_shadows::<M>
298                        .in_set(RenderSet::QueueMeshes)
299                        .after(prepare_assets::<PreparedMaterial<M>>),
300                );
301            }
302
303            #[cfg(feature = "meshlet")]
304            render_app.add_systems(
305                Render,
306                queue_material_meshlet_meshes::<M>
307                    .in_set(RenderSet::QueueMeshes)
308                    .run_if(resource_exists::<InstanceManager>),
309            );
310
311            #[cfg(feature = "meshlet")]
312            render_app.add_systems(
313                Render,
314                prepare_material_meshlet_meshes_main_opaque_pass::<M>
315                    .in_set(RenderSet::QueueMeshes)
316                    .after(prepare_assets::<PreparedMaterial<M>>)
317                    .before(queue_material_meshlet_meshes::<M>)
318                    .run_if(resource_exists::<InstanceManager>),
319            );
320        }
321
322        if self.shadows_enabled || self.prepass_enabled {
323            // PrepassPipelinePlugin is required for shadow mapping and the optional PrepassPlugin
324            app.add_plugins(PrepassPipelinePlugin::<M>::default());
325        }
326
327        if self.prepass_enabled {
328            app.add_plugins(PrepassPlugin::<M>::default());
329        }
330    }
331
332    fn finish(&self, app: &mut App) {
333        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
334            render_app.init_resource::<MaterialPipeline<M>>();
335        }
336    }
337}
338
339/// A key uniquely identifying a specialized [`MaterialPipeline`].
340pub struct MaterialPipelineKey<M: Material> {
341    pub mesh_key: MeshPipelineKey,
342    pub bind_group_data: M::Data,
343}
344
345impl<M: Material> Eq for MaterialPipelineKey<M> where M::Data: PartialEq {}
346
347impl<M: Material> PartialEq for MaterialPipelineKey<M>
348where
349    M::Data: PartialEq,
350{
351    fn eq(&self, other: &Self) -> bool {
352        self.mesh_key == other.mesh_key && self.bind_group_data == other.bind_group_data
353    }
354}
355
356impl<M: Material> Clone for MaterialPipelineKey<M>
357where
358    M::Data: Clone,
359{
360    fn clone(&self) -> Self {
361        Self {
362            mesh_key: self.mesh_key,
363            bind_group_data: self.bind_group_data.clone(),
364        }
365    }
366}
367
368impl<M: Material> Hash for MaterialPipelineKey<M>
369where
370    M::Data: Hash,
371{
372    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
373        self.mesh_key.hash(state);
374        self.bind_group_data.hash(state);
375    }
376}
377
378/// Render pipeline data for a given [`Material`].
379#[derive(Resource)]
380pub struct MaterialPipeline<M: Material> {
381    pub mesh_pipeline: MeshPipeline,
382    pub material_layout: BindGroupLayout,
383    pub vertex_shader: Option<Handle<Shader>>,
384    pub fragment_shader: Option<Handle<Shader>>,
385    pub marker: PhantomData<M>,
386}
387
388impl<M: Material> Clone for MaterialPipeline<M> {
389    fn clone(&self) -> Self {
390        Self {
391            mesh_pipeline: self.mesh_pipeline.clone(),
392            material_layout: self.material_layout.clone(),
393            vertex_shader: self.vertex_shader.clone(),
394            fragment_shader: self.fragment_shader.clone(),
395            marker: PhantomData,
396        }
397    }
398}
399
400impl<M: Material> SpecializedMeshPipeline for MaterialPipeline<M>
401where
402    M::Data: PartialEq + Eq + Hash + Clone,
403{
404    type Key = MaterialPipelineKey<M>;
405
406    fn specialize(
407        &self,
408        key: Self::Key,
409        layout: &MeshVertexBufferLayoutRef,
410    ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
411        let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?;
412        if let Some(vertex_shader) = &self.vertex_shader {
413            descriptor.vertex.shader = vertex_shader.clone();
414        }
415
416        if let Some(fragment_shader) = &self.fragment_shader {
417            descriptor.fragment.as_mut().unwrap().shader = fragment_shader.clone();
418        }
419
420        descriptor.layout.insert(2, self.material_layout.clone());
421
422        M::specialize(self, &mut descriptor, layout, key)?;
423        Ok(descriptor)
424    }
425}
426
427impl<M: Material> FromWorld for MaterialPipeline<M> {
428    fn from_world(world: &mut World) -> Self {
429        let asset_server = world.resource::<AssetServer>();
430        let render_device = world.resource::<RenderDevice>();
431
432        MaterialPipeline {
433            mesh_pipeline: world.resource::<MeshPipeline>().clone(),
434            material_layout: M::bind_group_layout(render_device),
435            vertex_shader: match M::vertex_shader() {
436                ShaderRef::Default => None,
437                ShaderRef::Handle(handle) => Some(handle),
438                ShaderRef::Path(path) => Some(asset_server.load(path)),
439            },
440            fragment_shader: match M::fragment_shader() {
441                ShaderRef::Default => None,
442                ShaderRef::Handle(handle) => Some(handle),
443                ShaderRef::Path(path) => Some(asset_server.load(path)),
444            },
445            marker: PhantomData,
446        }
447    }
448}
449
450type DrawMaterial<M> = (
451    SetItemPipeline,
452    SetMeshViewBindGroup<0>,
453    SetMeshBindGroup<1>,
454    SetMaterialBindGroup<M, 2>,
455    DrawMesh,
456);
457
458/// Sets the bind group for a given [`Material`] at the configured `I` index.
459pub struct SetMaterialBindGroup<M: Material, const I: usize>(PhantomData<M>);
460impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterialBindGroup<M, I> {
461    type Param = (
462        SRes<RenderAssets<PreparedMaterial<M>>>,
463        SRes<RenderMaterialInstances<M>>,
464    );
465    type ViewQuery = ();
466    type ItemQuery = ();
467
468    #[inline]
469    fn render<'w>(
470        item: &P,
471        _view: (),
472        _item_query: Option<()>,
473        (materials, material_instances): SystemParamItem<'w, '_, Self::Param>,
474        pass: &mut TrackedRenderPass<'w>,
475    ) -> RenderCommandResult {
476        let materials = materials.into_inner();
477        let material_instances = material_instances.into_inner();
478
479        let Some(material_asset_id) = material_instances.get(&item.main_entity()) else {
480            return RenderCommandResult::Skip;
481        };
482        let Some(material) = materials.get(*material_asset_id) else {
483            return RenderCommandResult::Skip;
484        };
485        pass.set_bind_group(I, &material.bind_group, &[]);
486        RenderCommandResult::Success
487    }
488}
489
490/// Stores all extracted instances of a [`Material`] in the render world.
491#[derive(Resource, Deref, DerefMut)]
492pub struct RenderMaterialInstances<M: Material>(pub MainEntityHashMap<AssetId<M>>);
493
494impl<M: Material> Default for RenderMaterialInstances<M> {
495    fn default() -> Self {
496        Self(Default::default())
497    }
498}
499
500pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey {
501    match alpha_mode {
502        // Premultiplied and Add share the same pipeline key
503        // They're made distinct in the PBR shader, via `premultiply_alpha()`
504        AlphaMode::Premultiplied | AlphaMode::Add => MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA,
505        AlphaMode::Blend => MeshPipelineKey::BLEND_ALPHA,
506        AlphaMode::Multiply => MeshPipelineKey::BLEND_MULTIPLY,
507        AlphaMode::Mask(_) => MeshPipelineKey::MAY_DISCARD,
508        AlphaMode::AlphaToCoverage => match *msaa {
509            Msaa::Off => MeshPipelineKey::MAY_DISCARD,
510            _ => MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE,
511        },
512        _ => MeshPipelineKey::NONE,
513    }
514}
515
516pub const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> MeshPipelineKey {
517    match tonemapping {
518        Tonemapping::None => MeshPipelineKey::TONEMAP_METHOD_NONE,
519        Tonemapping::Reinhard => MeshPipelineKey::TONEMAP_METHOD_REINHARD,
520        Tonemapping::ReinhardLuminance => MeshPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE,
521        Tonemapping::AcesFitted => MeshPipelineKey::TONEMAP_METHOD_ACES_FITTED,
522        Tonemapping::AgX => MeshPipelineKey::TONEMAP_METHOD_AGX,
523        Tonemapping::SomewhatBoringDisplayTransform => {
524            MeshPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM
525        }
526        Tonemapping::TonyMcMapface => MeshPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE,
527        Tonemapping::BlenderFilmic => MeshPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC,
528    }
529}
530
531pub const fn screen_space_specular_transmission_pipeline_key(
532    screen_space_transmissive_blur_quality: ScreenSpaceTransmissionQuality,
533) -> MeshPipelineKey {
534    match screen_space_transmissive_blur_quality {
535        ScreenSpaceTransmissionQuality::Low => {
536            MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_LOW
537        }
538        ScreenSpaceTransmissionQuality::Medium => {
539            MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM
540        }
541        ScreenSpaceTransmissionQuality::High => {
542            MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH
543        }
544        ScreenSpaceTransmissionQuality::Ultra => {
545            MeshPipelineKey::SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA
546        }
547    }
548}
549
550pub fn extract_mesh_materials<M: Material>(
551    mut material_instances: ResMut<RenderMaterialInstances<M>>,
552    query: Extract<Query<(Entity, &ViewVisibility, &MeshMaterial3d<M>)>>,
553) {
554    material_instances.clear();
555
556    for (entity, view_visibility, material) in &query {
557        if view_visibility.get() {
558            material_instances.insert(entity.into(), material.id());
559        }
560    }
561}
562
563/// For each view, iterates over all the meshes visible from that view and adds
564/// them to [`BinnedRenderPhase`]s or [`SortedRenderPhase`]s as appropriate.
565#[allow(clippy::too_many_arguments)]
566pub fn queue_material_meshes<M: Material>(
567    (
568        opaque_draw_functions,
569        alpha_mask_draw_functions,
570        transmissive_draw_functions,
571        transparent_draw_functions,
572    ): (
573        Res<DrawFunctions<Opaque3d>>,
574        Res<DrawFunctions<AlphaMask3d>>,
575        Res<DrawFunctions<Transmissive3d>>,
576        Res<DrawFunctions<Transparent3d>>,
577    ),
578    material_pipeline: Res<MaterialPipeline<M>>,
579    mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
580    pipeline_cache: Res<PipelineCache>,
581    render_meshes: Res<RenderAssets<RenderMesh>>,
582    render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
583    render_mesh_instances: Res<RenderMeshInstances>,
584    render_material_instances: Res<RenderMaterialInstances<M>>,
585    render_lightmaps: Res<RenderLightmaps>,
586    render_visibility_ranges: Res<RenderVisibilityRanges>,
587    mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
588    mut alpha_mask_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
589    mut transmissive_render_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
590    mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
591    views: Query<(
592        Entity,
593        &ExtractedView,
594        &RenderVisibleEntities,
595        &Msaa,
596        Option<&Tonemapping>,
597        Option<&DebandDither>,
598        Option<&ShadowFilteringMethod>,
599        Has<ScreenSpaceAmbientOcclusion>,
600        (
601            Has<NormalPrepass>,
602            Has<DepthPrepass>,
603            Has<MotionVectorPrepass>,
604            Has<DeferredPrepass>,
605        ),
606        Option<&Camera3d>,
607        Has<TemporalJitter>,
608        Option<&Projection>,
609        (
610            Has<RenderViewLightProbes<EnvironmentMapLight>>,
611            Has<RenderViewLightProbes<IrradianceVolume>>,
612        ),
613        Has<OrderIndependentTransparencySettings>,
614    )>,
615) where
616    M::Data: PartialEq + Eq + Hash + Clone,
617{
618    for (
619        view_entity,
620        view,
621        visible_entities,
622        msaa,
623        tonemapping,
624        dither,
625        shadow_filter_method,
626        ssao,
627        (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
628        camera_3d,
629        temporal_jitter,
630        projection,
631        (has_environment_maps, has_irradiance_volumes),
632        has_oit,
633    ) in &views
634    {
635        let (
636            Some(opaque_phase),
637            Some(alpha_mask_phase),
638            Some(transmissive_phase),
639            Some(transparent_phase),
640        ) = (
641            opaque_render_phases.get_mut(&view_entity),
642            alpha_mask_render_phases.get_mut(&view_entity),
643            transmissive_render_phases.get_mut(&view_entity),
644            transparent_render_phases.get_mut(&view_entity),
645        )
646        else {
647            continue;
648        };
649
650        let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial<M>>();
651        let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial<M>>();
652        let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial<M>>();
653        let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial<M>>();
654
655        let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
656            | MeshPipelineKey::from_hdr(view.hdr);
657
658        if normal_prepass {
659            view_key |= MeshPipelineKey::NORMAL_PREPASS;
660        }
661
662        if depth_prepass {
663            view_key |= MeshPipelineKey::DEPTH_PREPASS;
664        }
665
666        if motion_vector_prepass {
667            view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
668        }
669
670        if deferred_prepass {
671            view_key |= MeshPipelineKey::DEFERRED_PREPASS;
672        }
673
674        if temporal_jitter {
675            view_key |= MeshPipelineKey::TEMPORAL_JITTER;
676        }
677
678        if has_environment_maps {
679            view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
680        }
681
682        if has_irradiance_volumes {
683            view_key |= MeshPipelineKey::IRRADIANCE_VOLUME;
684        }
685
686        if has_oit {
687            view_key |= MeshPipelineKey::OIT_ENABLED;
688        }
689
690        if let Some(projection) = projection {
691            view_key |= match projection {
692                Projection::Perspective(_) => MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE,
693                Projection::Orthographic(_) => MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC,
694            };
695        }
696
697        match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
698            ShadowFilteringMethod::Hardware2x2 => {
699                view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
700            }
701            ShadowFilteringMethod::Gaussian => {
702                view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_GAUSSIAN;
703            }
704            ShadowFilteringMethod::Temporal => {
705                view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_TEMPORAL;
706            }
707        }
708
709        if !view.hdr {
710            if let Some(tonemapping) = tonemapping {
711                view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
712                view_key |= tonemapping_pipeline_key(*tonemapping);
713            }
714            if let Some(DebandDither::Enabled) = dither {
715                view_key |= MeshPipelineKey::DEBAND_DITHER;
716            }
717        }
718        if ssao {
719            view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION;
720        }
721        if let Some(camera_3d) = camera_3d {
722            view_key |= screen_space_specular_transmission_pipeline_key(
723                camera_3d.screen_space_specular_transmission_quality,
724            );
725        }
726
727        let rangefinder = view.rangefinder3d();
728        for (render_entity, visible_entity) in visible_entities.iter::<With<Mesh3d>>() {
729            let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
730                continue;
731            };
732            let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
733            else {
734                continue;
735            };
736            let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
737                continue;
738            };
739            let Some(material) = render_materials.get(*material_asset_id) else {
740                continue;
741            };
742
743            let mut mesh_pipeline_key_bits = material.properties.mesh_pipeline_key_bits;
744            mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(
745                material.properties.alpha_mode,
746                msaa,
747            ));
748            let mut mesh_key = view_key
749                | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
750                | mesh_pipeline_key_bits;
751
752            let lightmap_image = render_lightmaps
753                .render_lightmaps
754                .get(visible_entity)
755                .map(|lightmap| lightmap.image);
756            if lightmap_image.is_some() {
757                mesh_key |= MeshPipelineKey::LIGHTMAPPED;
758            }
759
760            if render_visibility_ranges.entity_has_crossfading_visibility_ranges(*visible_entity) {
761                mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
762            }
763
764            if motion_vector_prepass {
765                // If the previous frame have skins or morph targets, note that.
766                if mesh_instance
767                    .flags
768                    .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
769                {
770                    mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
771                }
772                if mesh_instance
773                    .flags
774                    .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
775                {
776                    mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
777                }
778            }
779
780            let pipeline_id = pipelines.specialize(
781                &pipeline_cache,
782                &material_pipeline,
783                MaterialPipelineKey {
784                    mesh_key,
785                    bind_group_data: material.key.clone(),
786                },
787                &mesh.layout,
788            );
789            let pipeline_id = match pipeline_id {
790                Ok(id) => id,
791                Err(err) => {
792                    error!("{}", err);
793                    continue;
794                }
795            };
796
797            mesh_instance
798                .material_bind_group_id
799                .set(material.get_bind_group_id());
800
801            match mesh_key
802                .intersection(MeshPipelineKey::BLEND_RESERVED_BITS | MeshPipelineKey::MAY_DISCARD)
803            {
804                MeshPipelineKey::BLEND_OPAQUE | MeshPipelineKey::BLEND_ALPHA_TO_COVERAGE => {
805                    if material.properties.reads_view_transmission_texture {
806                        let distance = rangefinder.distance_translation(&mesh_instance.translation)
807                            + material.properties.depth_bias;
808                        transmissive_phase.add(Transmissive3d {
809                            entity: (*render_entity, *visible_entity),
810                            draw_function: draw_transmissive_pbr,
811                            pipeline: pipeline_id,
812                            distance,
813                            batch_range: 0..1,
814                            extra_index: PhaseItemExtraIndex::NONE,
815                        });
816                    } else if material.properties.render_method == OpaqueRendererMethod::Forward {
817                        let bin_key = Opaque3dBinKey {
818                            draw_function: draw_opaque_pbr,
819                            pipeline: pipeline_id,
820                            asset_id: mesh_instance.mesh_asset_id.into(),
821                            material_bind_group_id: material.get_bind_group_id().0,
822                            lightmap_image,
823                        };
824                        opaque_phase.add(
825                            bin_key,
826                            (*render_entity, *visible_entity),
827                            BinnedRenderPhaseType::mesh(mesh_instance.should_batch()),
828                        );
829                    }
830                }
831                // Alpha mask
832                MeshPipelineKey::MAY_DISCARD => {
833                    if material.properties.reads_view_transmission_texture {
834                        let distance = rangefinder.distance_translation(&mesh_instance.translation)
835                            + material.properties.depth_bias;
836                        transmissive_phase.add(Transmissive3d {
837                            entity: (*render_entity, *visible_entity),
838                            draw_function: draw_transmissive_pbr,
839                            pipeline: pipeline_id,
840                            distance,
841                            batch_range: 0..1,
842                            extra_index: PhaseItemExtraIndex::NONE,
843                        });
844                    } else if material.properties.render_method == OpaqueRendererMethod::Forward {
845                        let bin_key = OpaqueNoLightmap3dBinKey {
846                            draw_function: draw_alpha_mask_pbr,
847                            pipeline: pipeline_id,
848                            asset_id: mesh_instance.mesh_asset_id.into(),
849                            material_bind_group_id: material.get_bind_group_id().0,
850                        };
851                        alpha_mask_phase.add(
852                            bin_key,
853                            (*render_entity, *visible_entity),
854                            BinnedRenderPhaseType::mesh(mesh_instance.should_batch()),
855                        );
856                    }
857                }
858                _ => {
859                    let distance = rangefinder.distance_translation(&mesh_instance.translation)
860                        + material.properties.depth_bias;
861                    transparent_phase.add(Transparent3d {
862                        entity: (*render_entity, *visible_entity),
863                        draw_function: draw_transparent_pbr,
864                        pipeline: pipeline_id,
865                        distance,
866                        batch_range: 0..1,
867                        extra_index: PhaseItemExtraIndex::NONE,
868                    });
869                }
870            }
871        }
872    }
873}
874
875/// Default render method used for opaque materials.
876#[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)]
877#[reflect(Resource, Default, Debug)]
878pub struct DefaultOpaqueRendererMethod(OpaqueRendererMethod);
879
880impl DefaultOpaqueRendererMethod {
881    pub fn forward() -> Self {
882        DefaultOpaqueRendererMethod(OpaqueRendererMethod::Forward)
883    }
884
885    pub fn deferred() -> Self {
886        DefaultOpaqueRendererMethod(OpaqueRendererMethod::Deferred)
887    }
888
889    pub fn set_to_forward(&mut self) {
890        self.0 = OpaqueRendererMethod::Forward;
891    }
892
893    pub fn set_to_deferred(&mut self) {
894        self.0 = OpaqueRendererMethod::Deferred;
895    }
896}
897
898/// Render method used for opaque materials.
899///
900/// The forward rendering main pass draws each mesh entity and shades it according to its
901/// corresponding material and the lights that affect it. Some render features like Screen Space
902/// Ambient Occlusion require running depth and normal prepasses, that are 'deferred'-like
903/// prepasses over all mesh entities to populate depth and normal textures. This means that when
904/// using render features that require running prepasses, multiple passes over all visible geometry
905/// are required. This can be slow if there is a lot of geometry that cannot be batched into few
906/// draws.
907///
908/// Deferred rendering runs a prepass to gather not only geometric information like depth and
909/// normals, but also all the material properties like base color, emissive color, reflectance,
910/// metalness, etc, and writes them into a deferred 'g-buffer' texture. The deferred main pass is
911/// then a fullscreen pass that reads data from these textures and executes shading. This allows
912/// for one pass over geometry, but is at the cost of not being able to use MSAA, and has heavier
913/// bandwidth usage which can be unsuitable for low end mobile or other bandwidth-constrained devices.
914///
915/// If a material indicates `OpaqueRendererMethod::Auto`, `DefaultOpaqueRendererMethod` will be used.
916#[derive(Default, Clone, Copy, Debug, PartialEq, Reflect)]
917pub enum OpaqueRendererMethod {
918    #[default]
919    Forward,
920    Deferred,
921    Auto,
922}
923
924/// Common [`Material`] properties, calculated for a specific material instance.
925pub struct MaterialProperties {
926    /// Is this material should be rendered by the deferred renderer when.
927    /// [`AlphaMode::Opaque`] or [`AlphaMode::Mask`]
928    pub render_method: OpaqueRendererMethod,
929    /// The [`AlphaMode`] of this material.
930    pub alpha_mode: AlphaMode,
931    /// The bits in the [`MeshPipelineKey`] for this material.
932    ///
933    /// These are precalculated so that we can just "or" them together in
934    /// [`queue_material_meshes`].
935    pub mesh_pipeline_key_bits: MeshPipelineKey,
936    /// Add a bias to the view depth of the mesh which can be used to force a specific render order
937    /// for meshes with equal depth, to avoid z-fighting.
938    /// The bias is in depth-texture units so large values may be needed to overcome small depth differences.
939    pub depth_bias: f32,
940    /// Whether the material would like to read from [`ViewTransmissionTexture`](bevy_core_pipeline::core_3d::ViewTransmissionTexture).
941    ///
942    /// This allows taking color output from the [`Opaque3d`] pass as an input, (for screen-space transmission) but requires
943    /// rendering to take place in a separate [`Transmissive3d`] pass.
944    pub reads_view_transmission_texture: bool,
945}
946
947/// Data prepared for a [`Material`] instance.
948pub struct PreparedMaterial<T: Material> {
949    pub bindings: Vec<(u32, OwnedBindingResource)>,
950    pub bind_group: BindGroup,
951    pub key: T::Data,
952    pub properties: MaterialProperties,
953}
954
955impl<M: Material> RenderAsset for PreparedMaterial<M> {
956    type SourceAsset = M;
957
958    type Param = (
959        SRes<RenderDevice>,
960        SRes<MaterialPipeline<M>>,
961        SRes<DefaultOpaqueRendererMethod>,
962        M::Param,
963    );
964
965    fn prepare_asset(
966        material: Self::SourceAsset,
967        (render_device, pipeline, default_opaque_render_method, ref mut material_param): &mut SystemParamItem<Self::Param>,
968    ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
969        match material.as_bind_group(&pipeline.material_layout, render_device, material_param) {
970            Ok(prepared) => {
971                let method = match material.opaque_render_method() {
972                    OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
973                    OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
974                    OpaqueRendererMethod::Auto => default_opaque_render_method.0,
975                };
976                let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
977                mesh_pipeline_key_bits.set(
978                    MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
979                    material.reads_view_transmission_texture(),
980                );
981
982                Ok(PreparedMaterial {
983                    bindings: prepared.bindings,
984                    bind_group: prepared.bind_group,
985                    key: prepared.data,
986                    properties: MaterialProperties {
987                        alpha_mode: material.alpha_mode(),
988                        depth_bias: material.depth_bias(),
989                        reads_view_transmission_texture: mesh_pipeline_key_bits
990                            .contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE),
991                        render_method: method,
992                        mesh_pipeline_key_bits,
993                    },
994                })
995            }
996            Err(AsBindGroupError::RetryNextUpdate) => {
997                Err(PrepareAssetError::RetryNextUpdate(material))
998            }
999            Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
1000        }
1001    }
1002}
1003
1004#[derive(Component, Clone, Copy, Default, PartialEq, Eq, Deref, DerefMut)]
1005pub struct MaterialBindGroupId(pub Option<BindGroupId>);
1006
1007impl MaterialBindGroupId {
1008    pub fn new(id: BindGroupId) -> Self {
1009        Self(Some(id))
1010    }
1011}
1012
1013impl From<BindGroup> for MaterialBindGroupId {
1014    fn from(value: BindGroup) -> Self {
1015        Self::new(value.id())
1016    }
1017}
1018
1019/// An atomic version of [`MaterialBindGroupId`] that can be read from and written to
1020/// safely from multiple threads.
1021#[derive(Default)]
1022pub struct AtomicMaterialBindGroupId(AtomicU32);
1023
1024impl AtomicMaterialBindGroupId {
1025    /// Stores a value atomically. Uses [`Ordering::Relaxed`] so there is zero guarantee of ordering
1026    /// relative to other operations.
1027    ///
1028    /// See also:  [`AtomicU32::store`].
1029    pub fn set(&self, id: MaterialBindGroupId) {
1030        let id = if let Some(id) = id.0 {
1031            NonZero::<u32>::from(id).get()
1032        } else {
1033            0
1034        };
1035        self.0.store(id, Ordering::Relaxed);
1036    }
1037
1038    /// Loads a value atomically. Uses [`Ordering::Relaxed`] so there is zero guarantee of ordering
1039    /// relative to other operations.
1040    ///
1041    /// See also:  [`AtomicU32::load`].
1042    pub fn get(&self) -> MaterialBindGroupId {
1043        MaterialBindGroupId(
1044            NonZero::<u32>::new(self.0.load(Ordering::Relaxed)).map(BindGroupId::from),
1045        )
1046    }
1047}
1048
1049impl<T: Material> PreparedMaterial<T> {
1050    pub fn get_bind_group_id(&self) -> MaterialBindGroupId {
1051        MaterialBindGroupId(Some(self.bind_group.id()))
1052    }
1053}