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 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 [`bevy_camera::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 [`bevy_camera::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 [`bevy_camera::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 [`bevy_camera::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/// View matrices from the previous frame.
78///
79/// Useful for temporal rendering techniques that need access to last frame's camera data.
80#[derive(Component, ShaderType, Clone)]
81pub struct PreviousViewData {
82    pub view_from_world: Mat4,
83    pub clip_from_world: Mat4,
84    pub clip_from_view: Mat4,
85    pub world_from_clip: Mat4,
86    pub view_from_clip: Mat4,
87}
88
89#[derive(Resource, Default)]
90pub struct PreviousViewUniforms {
91    pub uniforms: DynamicUniformBuffer<PreviousViewData>,
92}
93
94#[derive(Component)]
95pub struct PreviousViewUniformOffset {
96    pub offset: u32,
97}
98
99/// Textures that are written to by the prepass.
100///
101/// This component will only be present if any of the relevant prepass components are also present.
102#[derive(Component)]
103pub struct ViewPrepassTextures {
104    /// The depth texture generated by the prepass.
105    /// Exists only if [`DepthPrepass`] is added to the [`ViewTarget`](bevy_render::view::ViewTarget)
106    pub depth: Option<ColorAttachment>,
107    /// The normals texture generated by the prepass.
108    /// Exists only if [`NormalPrepass`] is added to the [`ViewTarget`](bevy_render::view::ViewTarget)
109    pub normal: Option<ColorAttachment>,
110    /// The motion vectors texture generated by the prepass.
111    /// Exists only if [`MotionVectorPrepass`] is added to the `ViewTarget`
112    pub motion_vectors: Option<ColorAttachment>,
113    /// The deferred gbuffer generated by the deferred pass.
114    /// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
115    pub deferred: Option<ColorAttachment>,
116    /// A texture that specifies the deferred lighting pass id for a material.
117    /// Exists only if [`DeferredPrepass`] is added to the `ViewTarget`
118    pub deferred_lighting_pass_id: Option<ColorAttachment>,
119    /// The size of the textures.
120    pub size: Extent3d,
121}
122
123impl ViewPrepassTextures {
124    pub fn depth_view(&self) -> Option<&TextureView> {
125        self.depth.as_ref().map(|t| &t.texture.default_view)
126    }
127
128    pub fn normal_view(&self) -> Option<&TextureView> {
129        self.normal.as_ref().map(|t| &t.texture.default_view)
130    }
131
132    pub fn motion_vectors_view(&self) -> Option<&TextureView> {
133        self.motion_vectors
134            .as_ref()
135            .map(|t| &t.texture.default_view)
136    }
137
138    pub fn deferred_view(&self) -> Option<&TextureView> {
139        self.deferred.as_ref().map(|t| &t.texture.default_view)
140    }
141}
142
143/// Opaque phase of the 3D prepass.
144///
145/// Sorted by pipeline, then by mesh to improve batching.
146///
147/// Used to render all 3D meshes with materials that have no transparency.
148pub struct Opaque3dPrepass {
149    /// Determines which objects can be placed into a *batch set*.
150    ///
151    /// Objects in a single batch set can potentially be multi-drawn together,
152    /// if it's enabled and the current platform supports it.
153    pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
154    /// Information that separates items into bins.
155    pub bin_key: OpaqueNoLightmap3dBinKey,
156
157    /// An entity from which Bevy fetches data common to all instances in this
158    /// batch, such as the mesh.
159    pub representative_entity: (Entity, MainEntity),
160    pub batch_range: Range<u32>,
161    pub extra_index: PhaseItemExtraIndex,
162}
163
164/// Information that must be identical in order to place opaque meshes in the
165/// same *batch set* in the prepass and deferred pass.
166///
167/// A batch set is a set of batches that can be multi-drawn together, if
168/// multi-draw is in use.
169#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
170pub struct OpaqueNoLightmap3dBatchSetKey {
171    /// The ID of the GPU pipeline.
172    pub pipeline: CachedRenderPipelineId,
173
174    /// The function used to draw the mesh.
175    pub draw_function: DrawFunctionId,
176
177    /// The ID of a bind group specific to the material.
178    ///
179    /// In the case of PBR, this is the `MaterialBindGroupIndex`.
180    pub material_bind_group_index: Option<u32>,
181
182    /// The ID of the slab of GPU memory that contains vertex data.
183    ///
184    /// For non-mesh items, you can fill this with 0 if your items can be
185    /// multi-drawn, or with a unique value if they can't.
186    pub vertex_slab: SlabId,
187
188    /// The ID of the slab of GPU memory that contains index data, if present.
189    ///
190    /// For non-mesh items, you can safely fill this with `None`.
191    pub index_slab: Option<SlabId>,
192}
193
194impl PhaseItemBatchSetKey for OpaqueNoLightmap3dBatchSetKey {
195    fn indexed(&self) -> bool {
196        self.index_slab.is_some()
197    }
198}
199
200// TODO: Try interning these.
201/// The data used to bin each opaque 3D object in the prepass and deferred pass.
202#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
203pub struct OpaqueNoLightmap3dBinKey {
204    /// The ID of the asset.
205    pub asset_id: UntypedAssetId,
206}
207
208impl PhaseItem for Opaque3dPrepass {
209    #[inline]
210    fn entity(&self) -> Entity {
211        self.representative_entity.0
212    }
213
214    fn main_entity(&self) -> MainEntity {
215        self.representative_entity.1
216    }
217
218    #[inline]
219    fn draw_function(&self) -> DrawFunctionId {
220        self.batch_set_key.draw_function
221    }
222
223    #[inline]
224    fn batch_range(&self) -> &Range<u32> {
225        &self.batch_range
226    }
227
228    #[inline]
229    fn batch_range_mut(&mut self) -> &mut Range<u32> {
230        &mut self.batch_range
231    }
232
233    #[inline]
234    fn extra_index(&self) -> PhaseItemExtraIndex {
235        self.extra_index.clone()
236    }
237
238    #[inline]
239    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
240        (&mut self.batch_range, &mut self.extra_index)
241    }
242}
243
244impl BinnedPhaseItem for Opaque3dPrepass {
245    type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
246    type BinKey = OpaqueNoLightmap3dBinKey;
247
248    #[inline]
249    fn new(
250        batch_set_key: Self::BatchSetKey,
251        bin_key: Self::BinKey,
252        representative_entity: (Entity, MainEntity),
253        batch_range: Range<u32>,
254        extra_index: PhaseItemExtraIndex,
255    ) -> Self {
256        Opaque3dPrepass {
257            batch_set_key,
258            bin_key,
259            representative_entity,
260            batch_range,
261            extra_index,
262        }
263    }
264}
265
266impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
267    #[inline]
268    fn cached_pipeline(&self) -> CachedRenderPipelineId {
269        self.batch_set_key.pipeline
270    }
271}
272
273/// Alpha mask phase of the 3D prepass.
274///
275/// Sorted by pipeline, then by mesh to improve batching.
276///
277/// Used to render all meshes with a material with an alpha mask.
278pub struct AlphaMask3dPrepass {
279    /// Determines which objects can be placed into a *batch set*.
280    ///
281    /// Objects in a single batch set can potentially be multi-drawn together,
282    /// if it's enabled and the current platform supports it.
283    pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
284    /// Information that separates items into bins.
285    pub bin_key: OpaqueNoLightmap3dBinKey,
286    pub representative_entity: (Entity, MainEntity),
287    pub batch_range: Range<u32>,
288    pub extra_index: PhaseItemExtraIndex,
289}
290
291impl PhaseItem for AlphaMask3dPrepass {
292    #[inline]
293    fn entity(&self) -> Entity {
294        self.representative_entity.0
295    }
296
297    fn main_entity(&self) -> MainEntity {
298        self.representative_entity.1
299    }
300
301    #[inline]
302    fn draw_function(&self) -> DrawFunctionId {
303        self.batch_set_key.draw_function
304    }
305
306    #[inline]
307    fn batch_range(&self) -> &Range<u32> {
308        &self.batch_range
309    }
310
311    #[inline]
312    fn batch_range_mut(&mut self) -> &mut Range<u32> {
313        &mut self.batch_range
314    }
315
316    #[inline]
317    fn extra_index(&self) -> PhaseItemExtraIndex {
318        self.extra_index.clone()
319    }
320
321    #[inline]
322    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
323        (&mut self.batch_range, &mut self.extra_index)
324    }
325}
326
327impl BinnedPhaseItem for AlphaMask3dPrepass {
328    type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
329    type BinKey = OpaqueNoLightmap3dBinKey;
330
331    #[inline]
332    fn new(
333        batch_set_key: Self::BatchSetKey,
334        bin_key: Self::BinKey,
335        representative_entity: (Entity, MainEntity),
336        batch_range: Range<u32>,
337        extra_index: PhaseItemExtraIndex,
338    ) -> Self {
339        Self {
340            batch_set_key,
341            bin_key,
342            representative_entity,
343            batch_range,
344            extra_index,
345        }
346    }
347}
348
349impl CachedRenderPipelinePhaseItem for AlphaMask3dPrepass {
350    #[inline]
351    fn cached_pipeline(&self) -> CachedRenderPipelineId {
352        self.batch_set_key.pipeline
353    }
354}
355
356pub fn prepass_target_descriptors(
357    normal_prepass: bool,
358    motion_vector_prepass: bool,
359    deferred_prepass: bool,
360) -> Vec<Option<ColorTargetState>> {
361    vec![
362        normal_prepass.then_some(ColorTargetState {
363            format: NORMAL_PREPASS_FORMAT,
364            blend: None,
365            write_mask: ColorWrites::ALL,
366        }),
367        motion_vector_prepass.then_some(ColorTargetState {
368            format: MOTION_VECTOR_PREPASS_FORMAT,
369            blend: None,
370            write_mask: ColorWrites::ALL,
371        }),
372        deferred_prepass.then_some(ColorTargetState {
373            format: DEFERRED_PREPASS_FORMAT,
374            blend: None,
375            write_mask: ColorWrites::ALL,
376        }),
377        deferred_prepass.then_some(ColorTargetState {
378            format: DEFERRED_LIGHTING_PASS_ID_FORMAT,
379            blend: None,
380            write_mask: ColorWrites::ALL,
381        }),
382    ]
383}