bevy_core_pipeline/prepass/
mod.rs

1//! Run a prepass before the main pass to generate depth, normals, and/or motion vectors textures, sometimes called a thin g-buffer.
2//! These textures are useful for various screen-space effects and reducing overdraw in the main pass.
3//!
4//! The prepass only runs for opaque meshes or meshes with an alpha mask. Transparent meshes are ignored.
5//!
6//! To enable the prepass, you need to add a prepass component to a [`crate::prelude::Camera3d`].
7//!
8//! [`DepthPrepass`]
9//! [`NormalPrepass`]
10//! [`MotionVectorPrepass`]
11//!
12//! The textures are automatically added to the default mesh view bindings. You can also get the raw textures
13//! by querying the [`ViewPrepassTextures`] component on any camera with a prepass component.
14//!
15//! The depth prepass will always run and generate the depth buffer as a side effect, but it won't copy it
16//! to a separate texture unless the [`DepthPrepass`] is activated. This means that if any prepass component is present
17//! it will always create a depth buffer that will be used by the main pass.
18//!
19//! When using the default mesh view bindings you should be able to use `prepass_depth()`,
20//! `prepass_normal()`, and `prepass_motion_vector()` to load the related textures.
21//! These functions are defined in `bevy_pbr::prepass_utils`. See the `shader_prepass` example that shows how to use them.
22//!
23//! The prepass runs for each `Material`. You can control if the prepass should run per-material by setting the `prepass_enabled`
24//! flag on the `MaterialPlugin`.
25//!
26//! Currently only works for 3D.
27
28pub mod node;
29
30use core::ops::Range;
31
32use crate::deferred::{DEFERRED_LIGHTING_PASS_ID_FORMAT, DEFERRED_PREPASS_FORMAT};
33use bevy_asset::UntypedAssetId;
34use bevy_ecs::prelude::*;
35use bevy_math::Mat4;
36use bevy_reflect::{std_traits::ReflectDefault, Reflect};
37use bevy_render::mesh::allocator::SlabId;
38use bevy_render::render_phase::PhaseItemBatchSetKey;
39use bevy_render::sync_world::MainEntity;
40use bevy_render::{
41    render_phase::{
42        BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem,
43        PhaseItemExtraIndex,
44    },
45    render_resource::{
46        CachedRenderPipelineId, ColorTargetState, ColorWrites, DynamicUniformBuffer, Extent3d,
47        ShaderType, TextureFormat, TextureView,
48    },
49    texture::ColorAttachment,
50};
51
52pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm;
53pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float;
54
55/// If added to a [`crate::prelude::Camera3d`] then depth values will be copied to a separate texture available to the main pass.
56#[derive(Component, Default, Reflect, Clone)]
57#[reflect(Component, Default, Clone)]
58pub struct DepthPrepass;
59
60/// If added to a [`crate::prelude::Camera3d`] then vertex world normals will be copied to a separate texture available to the main pass.
61/// Normals will have normal map textures already applied.
62#[derive(Component, Default, Reflect, Clone)]
63#[reflect(Component, Default, Clone)]
64pub struct NormalPrepass;
65
66/// If added to a [`crate::prelude::Camera3d`] then screen space motion vectors will be copied to a separate texture available to the main pass.
67#[derive(Component, Default, Reflect, Clone)]
68#[reflect(Component, Default, Clone)]
69pub struct MotionVectorPrepass;
70
71/// If added to a [`crate::prelude::Camera3d`] then deferred materials will be rendered to the deferred gbuffer texture and will be available to subsequent passes.
72/// Note the default deferred lighting plugin also requires `DepthPrepass` to work correctly.
73#[derive(Component, Default, Reflect)]
74#[reflect(Component, Default)]
75pub struct DeferredPrepass;
76
77#[derive(Component, ShaderType, Clone)]
78pub struct PreviousViewData {
79    pub view_from_world: Mat4,
80    pub clip_from_world: Mat4,
81    pub clip_from_view: Mat4,
82}
83
84#[derive(Resource, Default)]
85pub struct PreviousViewUniforms {
86    pub uniforms: DynamicUniformBuffer<PreviousViewData>,
87}
88
89#[derive(Component)]
90pub struct PreviousViewUniformOffset {
91    pub offset: u32,
92}
93
94/// Textures that are written to by the prepass.
95///
96/// This component will only be present if any of the relevant prepass components are also present.
97#[derive(Component)]
98pub struct ViewPrepassTextures {
99    /// The depth texture generated by the prepass.
100    /// Exists only if [`DepthPrepass`] is added to the [`ViewTarget`](bevy_render::view::ViewTarget)
101    pub depth: Option<ColorAttachment>,
102    /// The normals texture generated by the prepass.
103    /// Exists only if [`NormalPrepass`] is added to the [`ViewTarget`](bevy_render::view::ViewTarget)
104    pub normal: Option<ColorAttachment>,
105    /// The motion vectors texture generated by the prepass.
106    /// Exists only if [`MotionVectorPrepass`] is added to the `ViewTarget`
107    pub motion_vectors: Option<ColorAttachment>,
108    /// The deferred gbuffer generated by the deferred pass.
109    /// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
110    pub deferred: Option<ColorAttachment>,
111    /// A texture that specifies the deferred lighting pass id for a material.
112    /// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
113    pub deferred_lighting_pass_id: Option<ColorAttachment>,
114    /// The size of the textures.
115    pub size: Extent3d,
116}
117
118impl ViewPrepassTextures {
119    pub fn depth_view(&self) -> Option<&TextureView> {
120        self.depth.as_ref().map(|t| &t.texture.default_view)
121    }
122
123    pub fn normal_view(&self) -> Option<&TextureView> {
124        self.normal.as_ref().map(|t| &t.texture.default_view)
125    }
126
127    pub fn motion_vectors_view(&self) -> Option<&TextureView> {
128        self.motion_vectors
129            .as_ref()
130            .map(|t| &t.texture.default_view)
131    }
132
133    pub fn deferred_view(&self) -> Option<&TextureView> {
134        self.deferred.as_ref().map(|t| &t.texture.default_view)
135    }
136}
137
138/// Opaque phase of the 3D prepass.
139///
140/// Sorted by pipeline, then by mesh to improve batching.
141///
142/// Used to render all 3D meshes with materials that have no transparency.
143pub struct Opaque3dPrepass {
144    /// Determines which objects can be placed into a *batch set*.
145    ///
146    /// Objects in a single batch set can potentially be multi-drawn together,
147    /// if it's enabled and the current platform supports it.
148    pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
149    /// Information that separates items into bins.
150    pub bin_key: OpaqueNoLightmap3dBinKey,
151
152    /// An entity from which Bevy fetches data common to all instances in this
153    /// batch, such as the mesh.
154    pub representative_entity: (Entity, MainEntity),
155    pub batch_range: Range<u32>,
156    pub extra_index: PhaseItemExtraIndex,
157}
158
159/// Information that must be identical in order to place opaque meshes in the
160/// same *batch set* in the prepass and deferred pass.
161///
162/// A batch set is a set of batches that can be multi-drawn together, if
163/// multi-draw is in use.
164#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
165pub struct OpaqueNoLightmap3dBatchSetKey {
166    /// The ID of the GPU pipeline.
167    pub pipeline: CachedRenderPipelineId,
168
169    /// The function used to draw the mesh.
170    pub draw_function: DrawFunctionId,
171
172    /// The ID of a bind group specific to the material.
173    ///
174    /// In the case of PBR, this is the `MaterialBindGroupIndex`.
175    pub material_bind_group_index: Option<u32>,
176
177    /// The ID of the slab of GPU memory that contains vertex data.
178    ///
179    /// For non-mesh items, you can fill this with 0 if your items can be
180    /// multi-drawn, or with a unique value if they can't.
181    pub vertex_slab: SlabId,
182
183    /// The ID of the slab of GPU memory that contains index data, if present.
184    ///
185    /// For non-mesh items, you can safely fill this with `None`.
186    pub index_slab: Option<SlabId>,
187}
188
189impl PhaseItemBatchSetKey for OpaqueNoLightmap3dBatchSetKey {
190    fn indexed(&self) -> bool {
191        self.index_slab.is_some()
192    }
193}
194
195// TODO: Try interning these.
196/// The data used to bin each opaque 3D object in the prepass and deferred pass.
197#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
198pub struct OpaqueNoLightmap3dBinKey {
199    /// The ID of the asset.
200    pub asset_id: UntypedAssetId,
201}
202
203impl PhaseItem for Opaque3dPrepass {
204    #[inline]
205    fn entity(&self) -> Entity {
206        self.representative_entity.0
207    }
208
209    fn main_entity(&self) -> MainEntity {
210        self.representative_entity.1
211    }
212
213    #[inline]
214    fn draw_function(&self) -> DrawFunctionId {
215        self.batch_set_key.draw_function
216    }
217
218    #[inline]
219    fn batch_range(&self) -> &Range<u32> {
220        &self.batch_range
221    }
222
223    #[inline]
224    fn batch_range_mut(&mut self) -> &mut Range<u32> {
225        &mut self.batch_range
226    }
227
228    #[inline]
229    fn extra_index(&self) -> PhaseItemExtraIndex {
230        self.extra_index.clone()
231    }
232
233    #[inline]
234    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
235        (&mut self.batch_range, &mut self.extra_index)
236    }
237}
238
239impl BinnedPhaseItem for Opaque3dPrepass {
240    type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
241    type BinKey = OpaqueNoLightmap3dBinKey;
242
243    #[inline]
244    fn new(
245        batch_set_key: Self::BatchSetKey,
246        bin_key: Self::BinKey,
247        representative_entity: (Entity, MainEntity),
248        batch_range: Range<u32>,
249        extra_index: PhaseItemExtraIndex,
250    ) -> Self {
251        Opaque3dPrepass {
252            batch_set_key,
253            bin_key,
254            representative_entity,
255            batch_range,
256            extra_index,
257        }
258    }
259}
260
261impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
262    #[inline]
263    fn cached_pipeline(&self) -> CachedRenderPipelineId {
264        self.batch_set_key.pipeline
265    }
266}
267
268/// Alpha mask phase of the 3D prepass.
269///
270/// Sorted by pipeline, then by mesh to improve batching.
271///
272/// Used to render all meshes with a material with an alpha mask.
273pub struct AlphaMask3dPrepass {
274    /// Determines which objects can be placed into a *batch set*.
275    ///
276    /// Objects in a single batch set can potentially be multi-drawn together,
277    /// if it's enabled and the current platform supports it.
278    pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
279    /// Information that separates items into bins.
280    pub bin_key: OpaqueNoLightmap3dBinKey,
281    pub representative_entity: (Entity, MainEntity),
282    pub batch_range: Range<u32>,
283    pub extra_index: PhaseItemExtraIndex,
284}
285
286impl PhaseItem for AlphaMask3dPrepass {
287    #[inline]
288    fn entity(&self) -> Entity {
289        self.representative_entity.0
290    }
291
292    fn main_entity(&self) -> MainEntity {
293        self.representative_entity.1
294    }
295
296    #[inline]
297    fn draw_function(&self) -> DrawFunctionId {
298        self.batch_set_key.draw_function
299    }
300
301    #[inline]
302    fn batch_range(&self) -> &Range<u32> {
303        &self.batch_range
304    }
305
306    #[inline]
307    fn batch_range_mut(&mut self) -> &mut Range<u32> {
308        &mut self.batch_range
309    }
310
311    #[inline]
312    fn extra_index(&self) -> PhaseItemExtraIndex {
313        self.extra_index.clone()
314    }
315
316    #[inline]
317    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
318        (&mut self.batch_range, &mut self.extra_index)
319    }
320}
321
322impl BinnedPhaseItem for AlphaMask3dPrepass {
323    type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
324    type BinKey = OpaqueNoLightmap3dBinKey;
325
326    #[inline]
327    fn new(
328        batch_set_key: Self::BatchSetKey,
329        bin_key: Self::BinKey,
330        representative_entity: (Entity, MainEntity),
331        batch_range: Range<u32>,
332        extra_index: PhaseItemExtraIndex,
333    ) -> Self {
334        Self {
335            batch_set_key,
336            bin_key,
337            representative_entity,
338            batch_range,
339            extra_index,
340        }
341    }
342}
343
344impl CachedRenderPipelinePhaseItem for AlphaMask3dPrepass {
345    #[inline]
346    fn cached_pipeline(&self) -> CachedRenderPipelineId {
347        self.batch_set_key.pipeline
348    }
349}
350
351pub fn prepass_target_descriptors(
352    normal_prepass: bool,
353    motion_vector_prepass: bool,
354    deferred_prepass: bool,
355) -> Vec<Option<ColorTargetState>> {
356    vec![
357        normal_prepass.then_some(ColorTargetState {
358            format: NORMAL_PREPASS_FORMAT,
359            blend: None,
360            write_mask: ColorWrites::ALL,
361        }),
362        motion_vector_prepass.then_some(ColorTargetState {
363            format: MOTION_VECTOR_PREPASS_FORMAT,
364            blend: None,
365            write_mask: ColorWrites::ALL,
366        }),
367        deferred_prepass.then_some(ColorTargetState {
368            format: DEFERRED_PREPASS_FORMAT,
369            blend: None,
370            write_mask: ColorWrites::ALL,
371        }),
372        deferred_prepass.then_some(ColorTargetState {
373            format: DEFERRED_LIGHTING_PASS_ID_FORMAT,
374            blend: None,
375            write_mask: ColorWrites::ALL,
376        }),
377    ]
378}