Path: blob/main/crates/bevy_pbr/src/atmosphere/environment.rs
9421 views
use crate::{1resources::{2AtmosphereSampler, AtmosphereTextures, AtmosphereTransform, AtmosphereTransforms,3AtmosphereTransformsOffset, GpuAtmosphere,4},5ExtractedAtmosphere, GpuAtmosphereSettings, GpuLights, LightMeta, ViewLightsUniformOffset,6};7use bevy_asset::{load_embedded_asset, AssetServer, Assets, Handle, RenderAssetUsages};8use bevy_ecs::{9component::Component,10entity::Entity,11query::{With, Without},12resource::Resource,13system::{Commands, Query, Res, ResMut},14};15use bevy_image::Image;16use bevy_light::{AtmosphereEnvironmentMapLight, GeneratedEnvironmentMapLight};17use bevy_math::{Quat, UVec2};18use bevy_render::{19extract_component::{ComponentUniforms, DynamicUniformIndex, ExtractComponent},20render_asset::RenderAssets,21render_resource::{binding_types::*, *},22renderer::{RenderContext, RenderDevice, ViewQuery},23texture::{CachedTexture, GpuImage},24view::{ViewUniform, ViewUniformOffset, ViewUniforms},25};26use bevy_utils::default;27use tracing::warn;2829// Render world representation of an environment map light for the atmosphere30#[derive(Component, ExtractComponent, Clone)]31pub struct AtmosphereEnvironmentMap {32pub environment_map: Handle<Image>,33pub size: UVec2,34}3536#[derive(Component)]37pub struct AtmosphereProbeTextures {38pub environment: TextureView,39pub transmittance_lut: CachedTexture,40pub multiscattering_lut: CachedTexture,41pub sky_view_lut: CachedTexture,42pub aerial_view_lut: CachedTexture,43}4445#[derive(Component)]46pub(crate) struct AtmosphereProbeBindGroups {47pub environment: BindGroup,48}4950#[derive(Resource)]51pub struct AtmosphereProbeLayouts {52pub environment: BindGroupLayoutDescriptor,53}5455#[derive(Resource)]56pub struct AtmosphereProbePipeline {57pub environment: CachedComputePipelineId,58}5960pub fn init_atmosphere_probe_layout(mut commands: Commands) {61let environment = BindGroupLayoutDescriptor::new(62"environment_bind_group_layout",63&BindGroupLayoutEntries::with_indices(64ShaderStages::COMPUTE,65(66// uniforms67(0, uniform_buffer::<GpuAtmosphere>(true)),68(1, uniform_buffer::<GpuAtmosphereSettings>(true)),69(2, uniform_buffer::<AtmosphereTransform>(true)),70(3, uniform_buffer::<ViewUniform>(true)),71(4, uniform_buffer::<GpuLights>(true)),72// atmosphere luts and sampler73(8, texture_2d(TextureSampleType::default())), // transmittance74(9, texture_2d(TextureSampleType::default())), // multiscattering75(10, texture_2d(TextureSampleType::default())), // sky view76(11, texture_3d(TextureSampleType::default())), // aerial view77(12, sampler(SamplerBindingType::Filtering)),78// output 2D array texture79(8013,81texture_storage_2d_array(82TextureFormat::Rgba16Float,83StorageTextureAccess::WriteOnly,84),85),86),87),88);8990commands.insert_resource(AtmosphereProbeLayouts { environment });91}9293pub(super) fn prepare_atmosphere_probe_bind_groups(94probes: Query<(Entity, &AtmosphereProbeTextures), With<AtmosphereEnvironmentMap>>,95render_device: Res<RenderDevice>,96layouts: Res<AtmosphereProbeLayouts>,97atmosphere_sampler: Res<AtmosphereSampler>,98view_uniforms: Res<ViewUniforms>,99lights_uniforms: Res<LightMeta>,100atmosphere_transforms: Res<AtmosphereTransforms>,101atmosphere_uniforms: Res<ComponentUniforms<GpuAtmosphere>>,102settings_uniforms: Res<ComponentUniforms<GpuAtmosphereSettings>>,103pipeline_cache: Res<PipelineCache>,104mut commands: Commands,105) {106for (entity, textures) in &probes {107let environment = render_device.create_bind_group(108"environment_bind_group",109&pipeline_cache.get_bind_group_layout(&layouts.environment),110&BindGroupEntries::with_indices((111// uniforms112(0, atmosphere_uniforms.binding().unwrap()),113(1, settings_uniforms.binding().unwrap()),114(2, atmosphere_transforms.uniforms().binding().unwrap()),115(3, view_uniforms.uniforms.binding().unwrap()),116(4, lights_uniforms.view_gpu_lights.binding().unwrap()),117// atmosphere luts and sampler118(8, &textures.transmittance_lut.default_view),119(9, &textures.multiscattering_lut.default_view),120(10, &textures.sky_view_lut.default_view),121(11, &textures.aerial_view_lut.default_view),122(12, &**atmosphere_sampler),123// output 2D array texture124(13, &textures.environment),125)),126);127128commands129.entity(entity)130.insert(AtmosphereProbeBindGroups { environment });131}132}133134pub(super) fn prepare_probe_textures(135view_textures: Query<&AtmosphereTextures, With<ExtractedAtmosphere>>,136probes: Query<137(Entity, &AtmosphereEnvironmentMap),138(139With<AtmosphereEnvironmentMap>,140Without<AtmosphereProbeTextures>,141),142>,143gpu_images: Res<RenderAssets<GpuImage>>,144mut commands: Commands,145) {146for (probe, render_env_map) in &probes {147let environment = gpu_images.get(&render_env_map.environment_map).unwrap();148// create a cube view149let environment_view = environment.texture.create_view(&TextureViewDescriptor {150dimension: Some(TextureViewDimension::D2Array),151..Default::default()152});153// Get the first view entity's textures to borrow154if let Some(view_textures) = view_textures.iter().next() {155commands.entity(probe).insert(AtmosphereProbeTextures {156environment: environment_view,157transmittance_lut: view_textures.transmittance_lut.clone(),158multiscattering_lut: view_textures.multiscattering_lut.clone(),159sky_view_lut: view_textures.sky_view_lut.clone(),160aerial_view_lut: view_textures.aerial_view_lut.clone(),161});162}163}164}165166pub fn init_atmosphere_probe_pipeline(167pipeline_cache: Res<PipelineCache>,168layouts: Res<AtmosphereProbeLayouts>,169asset_server: Res<AssetServer>,170mut commands: Commands,171) {172let environment = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {173label: Some("environment_pipeline".into()),174layout: vec![layouts.environment.clone()],175shader: load_embedded_asset!(asset_server.as_ref(), "environment.wgsl"),176..default()177});178commands.insert_resource(AtmosphereProbePipeline { environment });179}180181// Ensure power-of-two dimensions to avoid edge update issues on cubemap faces182pub fn validate_environment_map_size(size: UVec2) -> UVec2 {183let new_size = UVec2::new(184size.x.max(1).next_power_of_two(),185size.y.max(1).next_power_of_two(),186);187if new_size != size {188warn!(189"Non-power-of-two AtmosphereEnvironmentMapLight size {}, correcting to {new_size}",190size191);192}193new_size194}195196pub fn prepare_atmosphere_probe_components(197probes: Query<(Entity, &AtmosphereEnvironmentMapLight), (Without<AtmosphereEnvironmentMap>,)>,198mut commands: Commands,199mut images: ResMut<Assets<Image>>,200) {201for (entity, env_map_light) in &probes {202// Create a cubemap image in the main world that we can reference203let new_size = validate_environment_map_size(env_map_light.size);204let mut environment_image = Image::new_fill(205Extent3d {206width: new_size.x,207height: new_size.y,208depth_or_array_layers: 6,209},210TextureDimension::D2,211&[0; 8],212TextureFormat::Rgba16Float,213RenderAssetUsages::all(),214);215216environment_image.texture_view_descriptor = Some(TextureViewDescriptor {217dimension: Some(TextureViewDimension::Cube),218..Default::default()219});220221environment_image.texture_descriptor.usage = TextureUsages::TEXTURE_BINDING222| TextureUsages::STORAGE_BINDING223| TextureUsages::COPY_SRC;224225// Add the image to assets to get a handle226let environment_handle = images.add(environment_image);227228commands.entity(entity).insert(AtmosphereEnvironmentMap {229environment_map: environment_handle.clone(),230size: new_size,231});232233commands234.entity(entity)235.insert(GeneratedEnvironmentMapLight {236environment_map: environment_handle,237intensity: env_map_light.intensity,238rotation: Quat::IDENTITY,239affects_lightmapped_mesh_diffuse: env_map_light.affects_lightmapped_mesh_diffuse,240});241}242}243pub fn atmosphere_environment(244view: ViewQuery<(245&DynamicUniformIndex<GpuAtmosphere>,246&DynamicUniformIndex<GpuAtmosphereSettings>,247&AtmosphereTransformsOffset,248&ViewUniformOffset,249&ViewLightsUniformOffset,250)>,251probe_query: Query<(&AtmosphereProbeBindGroups, &AtmosphereEnvironmentMap)>,252pipeline_cache: Res<PipelineCache>,253pipelines: Res<AtmosphereProbePipeline>,254mut ctx: RenderContext,255) {256let Some(environment_pipeline) = pipeline_cache.get_compute_pipeline(pipelines.environment)257else {258return;259};260261let (262atmosphere_uniforms_offset,263settings_uniforms_offset,264atmosphere_transforms_offset,265view_uniforms_offset,266lights_uniforms_offset,267) = view.into_inner();268269for (bind_groups, env_map_light) in probe_query.iter() {270let command_encoder = ctx.command_encoder();271let mut pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {272label: Some("environment_pass"),273timestamp_writes: None,274});275276pass.set_pipeline(environment_pipeline);277pass.set_bind_group(2780,279&bind_groups.environment,280&[281atmosphere_uniforms_offset.index(),282settings_uniforms_offset.index(),283atmosphere_transforms_offset.index(),284view_uniforms_offset.offset,285lights_uniforms_offset.offset,286],287);288289pass.dispatch_workgroups(290env_map_light.size.x / 8,291env_map_light.size.y / 8,2926, // 6 cubemap faces293);294}295}296297298