Path: blob/main/crates/bevy_pbr/src/transmission/node.rs
9368 views
use crate::{ScreenSpaceTransmission, Transmissive3d, ViewTransmissionTexture};12use bevy_camera::{MainPassResolutionOverride, Viewport};3use bevy_ecs::prelude::*;4use bevy_image::ToExtents;5use bevy_render::{6camera::ExtractedCamera,7diagnostic::RecordDiagnostics,8render_phase::ViewSortedRenderPhases,9render_resource::{RenderPassDescriptor, StoreOp},10renderer::{RenderContext, ViewQuery},11view::{ExtractedView, ViewDepthTexture, ViewTarget},12};13use core::ops::Range;14use tracing::error;15#[cfg(feature = "trace")]16use tracing::info_span;1718pub fn main_transmissive_pass_3d(19world: &World,20view: ViewQuery<(21&ExtractedCamera,22&ExtractedView,23&ScreenSpaceTransmission,24&ViewTarget,25Option<&ViewTransmissionTexture>,26&ViewDepthTexture,27Option<&MainPassResolutionOverride>,28)>,29transmissive_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,30mut ctx: RenderContext,31) {32let view_entity = view.entity();3334let (35camera,36extracted_view,37transmission_settings,38target,39transmission,40depth,41resolution_override,42) = view.into_inner();4344let Some(transmissive_phase) = transmissive_phases.get(&extracted_view.retained_view_entity)45else {46return;47};4849let Some(physical_target_size) = camera.physical_target_size else {50return;51};5253#[cfg(feature = "trace")]54let _main_transmissive_pass_3d_span = info_span!("main_transmissive_pass_3d").entered();5556let diagnostics = ctx.diagnostic_recorder();57let diagnostics = diagnostics.as_deref();5859let render_pass_descriptor = RenderPassDescriptor {60label: Some("main_transmissive_pass_3d"),61color_attachments: &[Some(target.get_color_attachment())],62depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),63timestamp_writes: None,64occlusion_query_set: None,65multiview_mask: None,66};6768if !transmissive_phase.items.is_empty() {69let steps = transmission_settings.steps;70if steps > 0 {71let transmission =72transmission.expect("`ViewTransmissionTexture` should exist at this point");7374// `transmissive_phase.items` are depth sorted, so we split them into N = `steps`75// ranges, rendering them back-to-front in multiple steps, allowing multiple levels of transparency.76for range in split_range(0..transmissive_phase.items.len(), steps) {77// Copy the main texture to the transmission texture78ctx.command_encoder().copy_texture_to_texture(79target.main_texture().as_image_copy(),80transmission.texture.as_image_copy(),81physical_target_size.to_extents(),82);8384let mut render_pass = ctx.begin_tracked_render_pass(render_pass_descriptor.clone());85let pass_span =86diagnostics.pass_span(&mut render_pass, "main_transmissive_pass_3d");8788if let Some(viewport) = camera.viewport.as_ref() {89render_pass.set_camera_viewport(viewport);90}9192if let Err(err) =93transmissive_phase.render_range(&mut render_pass, world, view_entity, range)94{95error!("Error encountered while rendering the transmissive phase {err:?}");96}9798pass_span.end(&mut render_pass);99}100} else {101let mut render_pass = ctx.begin_tracked_render_pass(render_pass_descriptor);102let pass_span = diagnostics.pass_span(&mut render_pass, "main_transmissive_pass_3d");103104if let Some(viewport) =105Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)106{107render_pass.set_camera_viewport(&viewport);108}109110if let Err(err) = transmissive_phase.render(&mut render_pass, world, view_entity) {111error!("Error encountered while rendering the transmissive phase {err:?}");112}113114pass_span.end(&mut render_pass);115}116}117}118119/// Splits a [`Range`] into at most `max_num_splits` sub-ranges without overlaps120///121/// Properly takes into account remainders of inexact divisions (by adding extra122/// elements to the initial sub-ranges as needed)123fn split_range(range: Range<usize>, max_num_splits: usize) -> impl Iterator<Item = Range<usize>> {124let len = range.end - range.start;125assert!(len > 0, "to be split, a range must not be empty");126assert!(max_num_splits > 0, "max_num_splits must be at least 1");127let num_splits = max_num_splits.min(len);128let step = len / num_splits;129let mut rem = len % num_splits;130let mut start = range.start;131132(0..num_splits).map(move |_| {133let extra = if rem > 0 {134rem -= 1;1351136} else {1370138};139let end = (start + step + extra).min(range.end);140let result = start..end;141start = end;142result143})144}145146147