Skip to main content

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 [`bevy_camera::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 background_motion_vectors;
29pub mod node;
30
31pub use background_motion_vectors::{
32    BackgroundMotionVectorsBindGroup, BackgroundMotionVectorsPipelineId,
33    BackgroundMotionVectorsPlugin, NoBackgroundMotionVectors,
34};
35use bevy_render::texture::DepthStencilAttachment;
36
37use core::ops::Range;
38
39use crate::deferred::{DEFERRED_LIGHTING_PASS_ID_FORMAT, DEFERRED_PREPASS_FORMAT};
40use bevy_asset::UntypedAssetId;
41use bevy_ecs::prelude::*;
42use bevy_math::Mat4;
43use bevy_reflect::{std_traits::ReflectDefault, Reflect};
44use bevy_render::mesh::allocator::MeshSlabs;
45use bevy_render::render_phase::PhaseItemBatchSetKey;
46use bevy_render::sync_world::MainEntity;
47use bevy_render::{
48    render_phase::{
49        BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem,
50        PhaseItemExtraIndex,
51    },
52    render_resource::{
53        CachedRenderPipelineId, ColorTargetState, ColorWrites, DynamicUniformBuffer, Extent3d,
54        ShaderType, TextureFormat, TextureView,
55    },
56    texture::ColorAttachment,
57};
58
59pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm;
60pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float;
61
62/// If added to a [`bevy_camera::Camera3d`] then depth values will be copied to a separate texture available to the main pass.
63#[derive(Component, Default, Reflect, Clone)]
64#[reflect(Component, Default, Clone)]
65pub struct DepthPrepass;
66
67/// If added to a [`bevy_camera::Camera3d`] then vertex world normals will be copied to a separate texture available to the main pass.
68/// Normals will have normal map textures already applied.
69#[derive(Component, Default, Reflect, Clone)]
70#[reflect(Component, Default, Clone)]
71pub struct NormalPrepass;
72
73/// If added to a [`bevy_camera::Camera3d`] then screen space motion vectors will be copied to a separate texture available to the main pass.
74///
75/// Motion vectors are stored in the range -1,1, with +x right and +y down.
76/// A value of (1.0,1.0) indicates a pixel moved from the top left corner to the bottom right corner of the screen.
77#[derive(Component, Default, Reflect, Clone)]
78#[reflect(Component, Default, Clone)]
79pub struct MotionVectorPrepass;
80
81/// If added to a [`bevy_camera::Camera3d`] then deferred materials will be rendered to the deferred gbuffer texture and will be available to subsequent passes.
82/// Note the default deferred lighting plugin also requires `DepthPrepass` to work correctly.
83#[derive(Component, Default, Reflect, Clone)]
84#[reflect(Component, Default, Clone)]
85pub struct DeferredPrepass;
86
87/// Allows querying the previous frame's [`DepthPrepass`].
88#[derive(Component, Default, Reflect, Clone)]
89#[reflect(Component, Default, Clone)]
90#[require(DepthPrepass)]
91pub struct DepthPrepassDoubleBuffer;
92
93/// Allows querying the previous frame's [`DeferredPrepass`].
94#[derive(Component, Default, Reflect, Clone)]
95#[reflect(Component, Default, Clone)]
96#[require(DeferredPrepass)]
97pub struct DeferredPrepassDoubleBuffer;
98
99/// View matrices from the previous frame.
100///
101/// Useful for temporal rendering techniques that need access to last frame's camera data.
102#[derive(Component, ShaderType, Clone)]
103pub struct PreviousViewData {
104    pub view_from_world: Mat4,
105    pub clip_from_world: Mat4,
106    pub unjittered_clip_from_world: Mat4,
107    pub clip_from_view: Mat4,
108    pub world_from_clip: Mat4,
109    pub view_from_clip: Mat4,
110}
111
112#[derive(Resource, Default)]
113pub struct PreviousViewUniforms {
114    pub uniforms: DynamicUniformBuffer<PreviousViewData>,
115}
116
117#[derive(Component)]
118pub struct PreviousViewUniformOffset {
119    pub offset: u32,
120}
121
122/// Textures that are written to by the prepass.
123///
124/// This component will only be present if any of the relevant prepass components are also present.
125#[derive(Component)]
126pub struct ViewPrepassTextures {
127    /// The depth texture generated by the prepass.
128    /// Exists only if [`DepthPrepass`] is added to the [`ViewTarget`](bevy_render::view::ViewTarget)
129    pub depth: Option<DepthStencilAttachment>,
130    /// The normals texture generated by the prepass.
131    /// Exists only if [`NormalPrepass`] is added to the [`ViewTarget`](bevy_render::view::ViewTarget)
132    pub normal: Option<ColorAttachment>,
133    /// The motion vectors texture generated by the prepass.
134    /// Exists only if [`MotionVectorPrepass`] is added to the `ViewTarget`
135    pub motion_vectors: Option<ColorAttachment>,
136    /// The deferred gbuffer generated by the deferred pass.
137    /// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
138    pub deferred: Option<ColorAttachment>,
139    /// A texture that specifies the deferred lighting pass id for a material.
140    /// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
141    pub deferred_lighting_pass_id: Option<ColorAttachment>,
142    /// The size of the textures.
143    pub size: Extent3d,
144}
145
146impl ViewPrepassTextures {
147    pub fn depth_only_view(&self) -> Option<&TextureView> {
148        self.depth
149            .as_ref()
150            .and_then(|t| t.depth_stencil_views().depth_only_view())
151    }
152
153    pub fn previous_depth_only_view(&self) -> Option<&TextureView> {
154        self.depth.as_ref().and_then(|t| {
155            t.previous_frame_depth_stencil_views
156                .as_ref()
157                .and_then(|views| views.depth_only_view())
158        })
159    }
160
161    pub fn normal_view(&self) -> Option<&TextureView> {
162        self.normal.as_ref().map(|t| &t.texture.default_view)
163    }
164
165    pub fn motion_vectors_view(&self) -> Option<&TextureView> {
166        self.motion_vectors
167            .as_ref()
168            .map(|t| &t.texture.default_view)
169    }
170
171    pub fn deferred_view(&self) -> Option<&TextureView> {
172        self.deferred.as_ref().map(|t| &t.texture.default_view)
173    }
174
175    pub fn previous_deferred_view(&self) -> Option<&TextureView> {
176        self.deferred
177            .as_ref()
178            .and_then(|t| t.previous_frame_texture.as_ref().map(|t| &t.default_view))
179    }
180}
181
182/// Opaque phase of the 3D prepass.
183///
184/// Sorted by pipeline, then by mesh to improve batching.
185///
186/// Used to render all 3D meshes with materials that have no transparency.
187pub struct Opaque3dPrepass {
188    /// Determines which objects can be placed into a *batch set*.
189    ///
190    /// Objects in a single batch set can potentially be multi-drawn together,
191    /// if it's enabled and the current platform supports it.
192    pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
193    /// Information that separates items into bins.
194    pub bin_key: OpaqueNoLightmap3dBinKey,
195
196    /// An entity from which Bevy fetches data common to all instances in this
197    /// batch, such as the mesh.
198    pub representative_entity: (Entity, MainEntity),
199    pub batch_range: Range<u32>,
200    pub extra_index: PhaseItemExtraIndex,
201}
202
203/// Information that must be identical in order to place opaque meshes in the
204/// same *batch set* in the prepass and deferred pass.
205///
206/// A batch set is a set of batches that can be multi-drawn together, if
207/// multi-draw is in use.
208#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
209pub struct OpaqueNoLightmap3dBatchSetKey {
210    /// The ID of the GPU pipeline.
211    pub pipeline: CachedRenderPipelineId,
212
213    /// The function used to draw the mesh.
214    pub draw_function: DrawFunctionId,
215
216    /// The ID of a bind group specific to the material.
217    ///
218    /// In the case of PBR, this is the `MaterialBindGroupIndex`.
219    pub material_bind_group_index: Option<u32>,
220
221    /// The IDs of the slabs of GPU memory in the mesh allocator that contain
222    /// the mesh data.
223    ///
224    /// For non-mesh items, you can fill the [`MeshSlabs::vertex_slab_id`] with
225    /// 0 if your items can be multi-drawn, or with a unique value if they
226    /// can't.
227    pub slabs: MeshSlabs,
228}
229
230impl PhaseItemBatchSetKey for OpaqueNoLightmap3dBatchSetKey {
231    fn indexed(&self) -> bool {
232        self.slabs.index_slab_id.is_some()
233    }
234}
235
236// TODO: Try interning these.
237/// The data used to bin each opaque 3D object in the prepass and deferred pass.
238#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
239pub struct OpaqueNoLightmap3dBinKey {
240    /// The ID of the asset.
241    pub asset_id: UntypedAssetId,
242}
243
244impl PhaseItem for Opaque3dPrepass {
245    #[inline]
246    fn entity(&self) -> Entity {
247        self.representative_entity.0
248    }
249
250    fn main_entity(&self) -> MainEntity {
251        self.representative_entity.1
252    }
253
254    #[inline]
255    fn draw_function(&self) -> DrawFunctionId {
256        self.batch_set_key.draw_function
257    }
258
259    #[inline]
260    fn batch_range(&self) -> &Range<u32> {
261        &self.batch_range
262    }
263
264    #[inline]
265    fn batch_range_mut(&mut self) -> &mut Range<u32> {
266        &mut self.batch_range
267    }
268
269    #[inline]
270    fn extra_index(&self) -> PhaseItemExtraIndex {
271        self.extra_index.clone()
272    }
273
274    #[inline]
275    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
276        (&mut self.batch_range, &mut self.extra_index)
277    }
278}
279
280impl BinnedPhaseItem for Opaque3dPrepass {
281    type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
282    type BinKey = OpaqueNoLightmap3dBinKey;
283
284    #[inline]
285    fn new(
286        batch_set_key: Self::BatchSetKey,
287        bin_key: Self::BinKey,
288        representative_entity: (Entity, MainEntity),
289        batch_range: Range<u32>,
290        extra_index: PhaseItemExtraIndex,
291    ) -> Self {
292        Opaque3dPrepass {
293            batch_set_key,
294            bin_key,
295            representative_entity,
296            batch_range,
297            extra_index,
298        }
299    }
300}
301
302impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
303    #[inline]
304    fn cached_pipeline(&self) -> CachedRenderPipelineId {
305        self.batch_set_key.pipeline
306    }
307}
308
309/// Alpha mask phase of the 3D prepass.
310///
311/// Sorted by pipeline, then by mesh to improve batching.
312///
313/// Used to render all meshes with a material with an alpha mask.
314pub struct AlphaMask3dPrepass {
315    /// Determines which objects can be placed into a *batch set*.
316    ///
317    /// Objects in a single batch set can potentially be multi-drawn together,
318    /// if it's enabled and the current platform supports it.
319    pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
320    /// Information that separates items into bins.
321    pub bin_key: OpaqueNoLightmap3dBinKey,
322    pub representative_entity: (Entity, MainEntity),
323    pub batch_range: Range<u32>,
324    pub extra_index: PhaseItemExtraIndex,
325}
326
327impl PhaseItem for AlphaMask3dPrepass {
328    #[inline]
329    fn entity(&self) -> Entity {
330        self.representative_entity.0
331    }
332
333    fn main_entity(&self) -> MainEntity {
334        self.representative_entity.1
335    }
336
337    #[inline]
338    fn draw_function(&self) -> DrawFunctionId {
339        self.batch_set_key.draw_function
340    }
341
342    #[inline]
343    fn batch_range(&self) -> &Range<u32> {
344        &self.batch_range
345    }
346
347    #[inline]
348    fn batch_range_mut(&mut self) -> &mut Range<u32> {
349        &mut self.batch_range
350    }
351
352    #[inline]
353    fn extra_index(&self) -> PhaseItemExtraIndex {
354        self.extra_index.clone()
355    }
356
357    #[inline]
358    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
359        (&mut self.batch_range, &mut self.extra_index)
360    }
361}
362
363impl BinnedPhaseItem for AlphaMask3dPrepass {
364    type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
365    type BinKey = OpaqueNoLightmap3dBinKey;
366
367    #[inline]
368    fn new(
369        batch_set_key: Self::BatchSetKey,
370        bin_key: Self::BinKey,
371        representative_entity: (Entity, MainEntity),
372        batch_range: Range<u32>,
373        extra_index: PhaseItemExtraIndex,
374    ) -> Self {
375        Self {
376            batch_set_key,
377            bin_key,
378            representative_entity,
379            batch_range,
380            extra_index,
381        }
382    }
383}
384
385impl CachedRenderPipelinePhaseItem for AlphaMask3dPrepass {
386    #[inline]
387    fn cached_pipeline(&self) -> CachedRenderPipelineId {
388        self.batch_set_key.pipeline
389    }
390}
391
392pub fn prepass_target_descriptors(
393    normal_prepass: bool,
394    motion_vector_prepass: bool,
395    deferred_prepass: bool,
396) -> Vec<Option<ColorTargetState>> {
397    vec![
398        normal_prepass.then_some(ColorTargetState {
399            format: NORMAL_PREPASS_FORMAT,
400            blend: None,
401            write_mask: ColorWrites::ALL,
402        }),
403        motion_vector_prepass.then_some(ColorTargetState {
404            format: MOTION_VECTOR_PREPASS_FORMAT,
405            blend: None,
406            write_mask: ColorWrites::ALL,
407        }),
408        deferred_prepass.then_some(ColorTargetState {
409            format: DEFERRED_PREPASS_FORMAT,
410            blend: None,
411            write_mask: ColorWrites::ALL,
412        }),
413        deferred_prepass.then_some(ColorTargetState {
414            format: DEFERRED_LIGHTING_PASS_ID_FORMAT,
415            blend: None,
416            write_mask: ColorWrites::ALL,
417        }),
418    ]
419}