Path: blob/main/crates/bevy_pbr/src/atmosphere/environment.rs
6604 views
use crate::{1resources::{2AtmosphereSamplers, AtmosphereTextures, AtmosphereTransform, AtmosphereTransforms,3AtmosphereTransformsOffset,4},5GpuAtmosphereSettings, GpuLights, LightMeta, ViewLightsUniformOffset,6};7use bevy_asset::{load_embedded_asset, AssetServer, Assets, Handle, RenderAssetUsages};8use bevy_ecs::{9component::Component,10entity::Entity,11query::{QueryState, With, Without},12resource::Resource,13system::{lifetimeless::Read, Commands, Query, Res, ResMut},14world::{FromWorld, World},15};16use bevy_image::Image;17use bevy_light::{AtmosphereEnvironmentMapLight, GeneratedEnvironmentMapLight};18use bevy_math::{Quat, UVec2};19use bevy_render::{20extract_component::{ComponentUniforms, DynamicUniformIndex, ExtractComponent},21render_asset::RenderAssets,22render_graph::{Node, NodeRunError, RenderGraphContext},23render_resource::{binding_types::*, *},24renderer::{RenderContext, RenderDevice},25texture::{CachedTexture, GpuImage},26view::{ViewUniform, ViewUniformOffset, ViewUniforms},27};28use bevy_utils::default;29use tracing::warn;3031use super::Atmosphere;3233// Render world representation of an environment map light for the atmosphere34#[derive(Component, ExtractComponent, Clone)]35pub struct AtmosphereEnvironmentMap {36pub environment_map: Handle<Image>,37pub size: UVec2,38}3940#[derive(Component)]41pub struct AtmosphereProbeTextures {42pub environment: TextureView,43pub transmittance_lut: CachedTexture,44pub multiscattering_lut: CachedTexture,45pub sky_view_lut: CachedTexture,46pub aerial_view_lut: CachedTexture,47}4849#[derive(Component)]50pub(crate) struct AtmosphereProbeBindGroups {51pub environment: BindGroup,52}5354#[derive(Resource)]55pub struct AtmosphereProbeLayouts {56pub environment: BindGroupLayout,57}5859#[derive(Resource)]60pub struct AtmosphereProbePipeline {61pub environment: CachedComputePipelineId,62}6364pub fn init_atmosphere_probe_layout(mut commands: Commands, render_device: Res<RenderDevice>) {65let environment = render_device.create_bind_group_layout(66"environment_bind_group_layout",67&BindGroupLayoutEntries::sequential(68ShaderStages::COMPUTE,69(70uniform_buffer::<Atmosphere>(true),71uniform_buffer::<GpuAtmosphereSettings>(true),72uniform_buffer::<AtmosphereTransform>(true),73uniform_buffer::<ViewUniform>(true),74uniform_buffer::<GpuLights>(true),75texture_2d(TextureSampleType::Float { filterable: true }), //transmittance lut and sampler76sampler(SamplerBindingType::Filtering),77texture_2d(TextureSampleType::Float { filterable: true }), //multiscattering lut and sampler78sampler(SamplerBindingType::Filtering),79texture_2d(TextureSampleType::Float { filterable: true }), //sky view lut and sampler80sampler(SamplerBindingType::Filtering),81texture_3d(TextureSampleType::Float { filterable: true }), //aerial view lut ans sampler82sampler(SamplerBindingType::Filtering),83texture_storage_2d_array(84// output 2D array texture85TextureFormat::Rgba16Float,86StorageTextureAccess::WriteOnly,87),88),89),90);9192commands.insert_resource(AtmosphereProbeLayouts { environment });93}9495pub(super) fn prepare_atmosphere_probe_bind_groups(96probes: Query<(Entity, &AtmosphereProbeTextures), With<AtmosphereEnvironmentMap>>,97render_device: Res<RenderDevice>,98layouts: Res<AtmosphereProbeLayouts>,99samplers: Res<AtmosphereSamplers>,100view_uniforms: Res<ViewUniforms>,101lights_uniforms: Res<LightMeta>,102atmosphere_transforms: Res<AtmosphereTransforms>,103atmosphere_uniforms: Res<ComponentUniforms<Atmosphere>>,104settings_uniforms: Res<ComponentUniforms<GpuAtmosphereSettings>>,105mut commands: Commands,106) {107for (entity, textures) in &probes {108let environment = render_device.create_bind_group(109"environment_bind_group",110&layouts.environment,111&BindGroupEntries::sequential((112atmosphere_uniforms.binding().unwrap(),113settings_uniforms.binding().unwrap(),114atmosphere_transforms.uniforms().binding().unwrap(),115view_uniforms.uniforms.binding().unwrap(),116lights_uniforms.view_gpu_lights.binding().unwrap(),117&textures.transmittance_lut.default_view,118&samplers.transmittance_lut,119&textures.multiscattering_lut.default_view,120&samplers.multiscattering_lut,121&textures.sky_view_lut.default_view,122&samplers.sky_view_lut,123&textures.aerial_view_lut.default_view,124&samplers.aerial_view_lut,125&textures.environment,126)),127);128129commands130.entity(entity)131.insert(AtmosphereProbeBindGroups { environment });132}133}134135pub(super) fn prepare_probe_textures(136view_textures: Query<&AtmosphereTextures, With<Atmosphere>>,137probes: Query<138(Entity, &AtmosphereEnvironmentMap),139(140With<AtmosphereEnvironmentMap>,141Without<AtmosphereProbeTextures>,142),143>,144gpu_images: Res<RenderAssets<GpuImage>>,145mut commands: Commands,146) {147for (probe, render_env_map) in &probes {148let environment = gpu_images.get(&render_env_map.environment_map).unwrap();149// create a cube view150let environment_view = environment.texture.create_view(&TextureViewDescriptor {151dimension: Some(TextureViewDimension::D2Array),152..Default::default()153});154// Get the first view entity's textures to borrow155if let Some(view_textures) = view_textures.iter().next() {156commands.entity(probe).insert(AtmosphereProbeTextures {157environment: environment_view,158transmittance_lut: view_textures.transmittance_lut.clone(),159multiscattering_lut: view_textures.multiscattering_lut.clone(),160sky_view_lut: view_textures.sky_view_lut.clone(),161aerial_view_lut: view_textures.aerial_view_lut.clone(),162});163}164}165}166167pub fn init_atmosphere_probe_pipeline(168pipeline_cache: Res<PipelineCache>,169layouts: Res<AtmosphereProbeLayouts>,170asset_server: Res<AssetServer>,171mut commands: Commands,172) {173let environment = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {174label: Some("environment_pipeline".into()),175layout: vec![layouts.environment.clone()],176shader: load_embedded_asset!(asset_server.as_ref(), "environment.wgsl"),177..default()178});179commands.insert_resource(AtmosphereProbePipeline { environment });180}181182// Ensure power-of-two dimensions to avoid edge update issues on cubemap faces183pub fn validate_environment_map_size(size: UVec2) -> UVec2 {184let new_size = UVec2::new(185size.x.max(1).next_power_of_two(),186size.y.max(1).next_power_of_two(),187);188if new_size != size {189warn!(190"Non-power-of-two AtmosphereEnvironmentMapLight size {}, correcting to {new_size}",191size192);193}194new_size195}196197pub fn prepare_atmosphere_probe_components(198probes: Query<(Entity, &AtmosphereEnvironmentMapLight), (Without<AtmosphereEnvironmentMap>,)>,199mut commands: Commands,200mut images: ResMut<Assets<Image>>,201) {202for (entity, env_map_light) in &probes {203// Create a cubemap image in the main world that we can reference204let new_size = validate_environment_map_size(env_map_light.size);205let mut environment_image = Image::new_fill(206Extent3d {207width: new_size.x,208height: new_size.y,209depth_or_array_layers: 6,210},211TextureDimension::D2,212&[0; 8],213TextureFormat::Rgba16Float,214RenderAssetUsages::all(),215);216217environment_image.texture_view_descriptor = Some(TextureViewDescriptor {218dimension: Some(TextureViewDimension::Cube),219..Default::default()220});221222environment_image.texture_descriptor.usage = TextureUsages::TEXTURE_BINDING223| TextureUsages::STORAGE_BINDING224| TextureUsages::COPY_SRC;225226// Add the image to assets to get a handle227let environment_handle = images.add(environment_image);228229commands.entity(entity).insert(AtmosphereEnvironmentMap {230environment_map: environment_handle.clone(),231size: new_size,232});233234commands235.entity(entity)236.insert(GeneratedEnvironmentMapLight {237environment_map: environment_handle,238intensity: env_map_light.intensity,239rotation: Quat::IDENTITY,240affects_lightmapped_mesh_diffuse: env_map_light.affects_lightmapped_mesh_diffuse,241});242}243}244245pub(super) struct EnvironmentNode {246main_view_query: QueryState<(247Read<DynamicUniformIndex<Atmosphere>>,248Read<DynamicUniformIndex<GpuAtmosphereSettings>>,249Read<AtmosphereTransformsOffset>,250Read<ViewUniformOffset>,251Read<ViewLightsUniformOffset>,252)>,253probe_query: QueryState<(254Read<AtmosphereProbeBindGroups>,255Read<AtmosphereEnvironmentMap>,256)>,257}258259impl FromWorld for EnvironmentNode {260fn from_world(world: &mut World) -> Self {261Self {262main_view_query: QueryState::new(world),263probe_query: QueryState::new(world),264}265}266}267268impl Node for EnvironmentNode {269fn update(&mut self, world: &mut World) {270self.main_view_query.update_archetypes(world);271self.probe_query.update_archetypes(world);272}273274fn run(275&self,276graph: &mut RenderGraphContext,277render_context: &mut RenderContext,278world: &World,279) -> Result<(), NodeRunError> {280let pipeline_cache = world.resource::<PipelineCache>();281let pipelines = world.resource::<AtmosphereProbePipeline>();282let view_entity = graph.view_entity();283284let Some(environment_pipeline) = pipeline_cache.get_compute_pipeline(pipelines.environment)285else {286return Ok(());287};288289let (Ok((290atmosphere_uniforms_offset,291settings_uniforms_offset,292atmosphere_transforms_offset,293view_uniforms_offset,294lights_uniforms_offset,295)),) = (self.main_view_query.get_manual(world, view_entity),)296else {297return Ok(());298};299300for (bind_groups, env_map_light) in self.probe_query.iter_manual(world) {301let mut pass =302render_context303.command_encoder()304.begin_compute_pass(&ComputePassDescriptor {305label: Some("environment_pass"),306timestamp_writes: None,307});308309pass.set_pipeline(environment_pipeline);310pass.set_bind_group(3110,312&bind_groups.environment,313&[314atmosphere_uniforms_offset.index(),315settings_uniforms_offset.index(),316atmosphere_transforms_offset.index(),317view_uniforms_offset.offset,318lights_uniforms_offset.offset,319],320);321322pass.dispatch_workgroups(323env_map_light.size.x / 8,324env_map_light.size.y / 8,3256, // 6 cubemap faces326);327}328329Ok(())330}331}332333334