Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/transmission/node.rs
9368 views
1
use crate::{ScreenSpaceTransmission, Transmissive3d, ViewTransmissionTexture};
2
3
use bevy_camera::{MainPassResolutionOverride, Viewport};
4
use bevy_ecs::prelude::*;
5
use bevy_image::ToExtents;
6
use 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
};
14
use core::ops::Range;
15
use tracing::error;
16
#[cfg(feature = "trace")]
17
use tracing::info_span;
18
19
pub 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)
124
fn 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
}
146
147