Skip to main content

bevy_pbr/transmission/
node.rs

1use crate::{ScreenSpaceTransmission, Transmissive3d, ViewTransmissionTexture};
2
3use bevy_camera::{MainPassResolutionOverride, Viewport};
4use bevy_ecs::prelude::*;
5use bevy_image::ToExtents;
6use bevy_render::{
7    camera::ExtractedCamera,
8    diagnostic::RecordDiagnostics,
9    render_phase::ViewSortedRenderPhases,
10    render_resource::{RenderPassDescriptor, StoreOp},
11    renderer::{RenderContext, ViewQuery},
12    view::{ExtractedView, ViewDepthTexture, ViewTarget},
13};
14use core::ops::Range;
15use tracing::error;
16#[cfg(feature = "trace")]
17use tracing::info_span;
18
19pub fn main_transmissive_pass_3d(
20    world: &World,
21    view: ViewQuery<(
22        &ExtractedCamera,
23        &ExtractedView,
24        &ScreenSpaceTransmission,
25        &ViewTarget,
26        Option<&ViewTransmissionTexture>,
27        &ViewDepthTexture,
28        Option<&MainPassResolutionOverride>,
29    )>,
30    transmissive_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
31    mut ctx: RenderContext,
32) {
33    let view_entity = view.entity();
34
35    let (
36        camera,
37        extracted_view,
38        transmission_settings,
39        target,
40        transmission,
41        depth,
42        resolution_override,
43    ) = view.into_inner();
44
45    let Some(transmissive_phase) = transmissive_phases.get(&extracted_view.retained_view_entity)
46    else {
47        return;
48    };
49
50    let Some(physical_target_size) = camera.physical_target_size else {
51        return;
52    };
53
54    #[cfg(feature = "trace")]
55    let _main_transmissive_pass_3d_span = info_span!("main_transmissive_pass_3d").entered();
56
57    let diagnostics = ctx.diagnostic_recorder();
58    let diagnostics = diagnostics.as_deref();
59
60    let render_pass_descriptor = RenderPassDescriptor {
61        label: Some("main_transmissive_pass_3d"),
62        color_attachments: &[Some(target.get_color_attachment())],
63        depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
64        timestamp_writes: None,
65        occlusion_query_set: None,
66        multiview_mask: None,
67    };
68
69    if !transmissive_phase.items.is_empty() {
70        let steps = transmission_settings.steps;
71        if steps > 0 {
72            let transmission =
73                transmission.expect("`ViewTransmissionTexture` should exist at this point");
74
75            // `transmissive_phase.items` are depth sorted, so we split them into N = `steps`
76            // ranges, rendering them back-to-front in multiple steps, allowing multiple levels of transparency.
77            for range in split_range(0..transmissive_phase.items.len(), steps) {
78                // Copy the main texture to the transmission texture
79                ctx.command_encoder().copy_texture_to_texture(
80                    target.main_texture().as_image_copy(),
81                    transmission.texture.as_image_copy(),
82                    physical_target_size.to_extents(),
83                );
84
85                let mut render_pass = ctx.begin_tracked_render_pass(render_pass_descriptor.clone());
86                let pass_span =
87                    diagnostics.pass_span(&mut render_pass, "main_transmissive_pass_3d");
88
89                if let Some(viewport) = camera.viewport.as_ref() {
90                    render_pass.set_camera_viewport(viewport);
91                }
92
93                if let Err(err) =
94                    transmissive_phase.render_range(&mut render_pass, world, view_entity, range)
95                {
96                    error!("Error encountered while rendering the transmissive phase {err:?}");
97                }
98
99                pass_span.end(&mut render_pass);
100            }
101        } else {
102            let mut render_pass = ctx.begin_tracked_render_pass(render_pass_descriptor);
103            let pass_span = diagnostics.pass_span(&mut render_pass, "main_transmissive_pass_3d");
104
105            if let Some(viewport) =
106                Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
107            {
108                render_pass.set_camera_viewport(&viewport);
109            }
110
111            if let Err(err) = transmissive_phase.render(&mut render_pass, world, view_entity) {
112                error!("Error encountered while rendering the transmissive phase {err:?}");
113            }
114
115            pass_span.end(&mut render_pass);
116        }
117    }
118}
119
120/// Splits a [`Range`] into at most `max_num_splits` sub-ranges without overlaps
121///
122/// Properly takes into account remainders of inexact divisions (by adding extra
123/// elements to the initial sub-ranges as needed)
124fn split_range(range: Range<usize>, max_num_splits: usize) -> impl Iterator<Item = Range<usize>> {
125    let len = range.end - range.start;
126    assert!(len > 0, "to be split, a range must not be empty");
127    assert!(max_num_splits > 0, "max_num_splits must be at least 1");
128    let num_splits = max_num_splits.min(len);
129    let step = len / num_splits;
130    let mut rem = len % num_splits;
131    let mut start = range.start;
132
133    (0..num_splits).map(move |_| {
134        let extra = if rem > 0 {
135            rem -= 1;
136            1
137        } else {
138            0
139        };
140        let end = (start + step + extra).min(range.end);
141        let result = start..end;
142        start = end;
143        result
144    })
145}