Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/atmosphere/environment.rs
9421 views
1
use crate::{
2
resources::{
3
AtmosphereSampler, AtmosphereTextures, AtmosphereTransform, AtmosphereTransforms,
4
AtmosphereTransformsOffset, GpuAtmosphere,
5
},
6
ExtractedAtmosphere, GpuAtmosphereSettings, GpuLights, LightMeta, ViewLightsUniformOffset,
7
};
8
use bevy_asset::{load_embedded_asset, AssetServer, Assets, Handle, RenderAssetUsages};
9
use bevy_ecs::{
10
component::Component,
11
entity::Entity,
12
query::{With, Without},
13
resource::Resource,
14
system::{Commands, Query, Res, ResMut},
15
};
16
use bevy_image::Image;
17
use bevy_light::{AtmosphereEnvironmentMapLight, GeneratedEnvironmentMapLight};
18
use bevy_math::{Quat, UVec2};
19
use bevy_render::{
20
extract_component::{ComponentUniforms, DynamicUniformIndex, ExtractComponent},
21
render_asset::RenderAssets,
22
render_resource::{binding_types::*, *},
23
renderer::{RenderContext, RenderDevice, ViewQuery},
24
texture::{CachedTexture, GpuImage},
25
view::{ViewUniform, ViewUniformOffset, ViewUniforms},
26
};
27
use bevy_utils::default;
28
use tracing::warn;
29
30
// Render world representation of an environment map light for the atmosphere
31
#[derive(Component, ExtractComponent, Clone)]
32
pub struct AtmosphereEnvironmentMap {
33
pub environment_map: Handle<Image>,
34
pub size: UVec2,
35
}
36
37
#[derive(Component)]
38
pub struct AtmosphereProbeTextures {
39
pub environment: TextureView,
40
pub transmittance_lut: CachedTexture,
41
pub multiscattering_lut: CachedTexture,
42
pub sky_view_lut: CachedTexture,
43
pub aerial_view_lut: CachedTexture,
44
}
45
46
#[derive(Component)]
47
pub(crate) struct AtmosphereProbeBindGroups {
48
pub environment: BindGroup,
49
}
50
51
#[derive(Resource)]
52
pub struct AtmosphereProbeLayouts {
53
pub environment: BindGroupLayoutDescriptor,
54
}
55
56
#[derive(Resource)]
57
pub struct AtmosphereProbePipeline {
58
pub environment: CachedComputePipelineId,
59
}
60
61
pub fn init_atmosphere_probe_layout(mut commands: Commands) {
62
let environment = BindGroupLayoutDescriptor::new(
63
"environment_bind_group_layout",
64
&BindGroupLayoutEntries::with_indices(
65
ShaderStages::COMPUTE,
66
(
67
// uniforms
68
(0, uniform_buffer::<GpuAtmosphere>(true)),
69
(1, uniform_buffer::<GpuAtmosphereSettings>(true)),
70
(2, uniform_buffer::<AtmosphereTransform>(true)),
71
(3, uniform_buffer::<ViewUniform>(true)),
72
(4, uniform_buffer::<GpuLights>(true)),
73
// atmosphere luts and sampler
74
(8, texture_2d(TextureSampleType::default())), // transmittance
75
(9, texture_2d(TextureSampleType::default())), // multiscattering
76
(10, texture_2d(TextureSampleType::default())), // sky view
77
(11, texture_3d(TextureSampleType::default())), // aerial view
78
(12, sampler(SamplerBindingType::Filtering)),
79
// output 2D array texture
80
(
81
13,
82
texture_storage_2d_array(
83
TextureFormat::Rgba16Float,
84
StorageTextureAccess::WriteOnly,
85
),
86
),
87
),
88
),
89
);
90
91
commands.insert_resource(AtmosphereProbeLayouts { environment });
92
}
93
94
pub(super) fn prepare_atmosphere_probe_bind_groups(
95
probes: Query<(Entity, &AtmosphereProbeTextures), With<AtmosphereEnvironmentMap>>,
96
render_device: Res<RenderDevice>,
97
layouts: Res<AtmosphereProbeLayouts>,
98
atmosphere_sampler: Res<AtmosphereSampler>,
99
view_uniforms: Res<ViewUniforms>,
100
lights_uniforms: Res<LightMeta>,
101
atmosphere_transforms: Res<AtmosphereTransforms>,
102
atmosphere_uniforms: Res<ComponentUniforms<GpuAtmosphere>>,
103
settings_uniforms: Res<ComponentUniforms<GpuAtmosphereSettings>>,
104
pipeline_cache: Res<PipelineCache>,
105
mut commands: Commands,
106
) {
107
for (entity, textures) in &probes {
108
let environment = render_device.create_bind_group(
109
"environment_bind_group",
110
&pipeline_cache.get_bind_group_layout(&layouts.environment),
111
&BindGroupEntries::with_indices((
112
// uniforms
113
(0, atmosphere_uniforms.binding().unwrap()),
114
(1, settings_uniforms.binding().unwrap()),
115
(2, atmosphere_transforms.uniforms().binding().unwrap()),
116
(3, view_uniforms.uniforms.binding().unwrap()),
117
(4, lights_uniforms.view_gpu_lights.binding().unwrap()),
118
// atmosphere luts and sampler
119
(8, &textures.transmittance_lut.default_view),
120
(9, &textures.multiscattering_lut.default_view),
121
(10, &textures.sky_view_lut.default_view),
122
(11, &textures.aerial_view_lut.default_view),
123
(12, &**atmosphere_sampler),
124
// output 2D array texture
125
(13, &textures.environment),
126
)),
127
);
128
129
commands
130
.entity(entity)
131
.insert(AtmosphereProbeBindGroups { environment });
132
}
133
}
134
135
pub(super) fn prepare_probe_textures(
136
view_textures: Query<&AtmosphereTextures, With<ExtractedAtmosphere>>,
137
probes: Query<
138
(Entity, &AtmosphereEnvironmentMap),
139
(
140
With<AtmosphereEnvironmentMap>,
141
Without<AtmosphereProbeTextures>,
142
),
143
>,
144
gpu_images: Res<RenderAssets<GpuImage>>,
145
mut commands: Commands,
146
) {
147
for (probe, render_env_map) in &probes {
148
let environment = gpu_images.get(&render_env_map.environment_map).unwrap();
149
// create a cube view
150
let environment_view = environment.texture.create_view(&TextureViewDescriptor {
151
dimension: Some(TextureViewDimension::D2Array),
152
..Default::default()
153
});
154
// Get the first view entity's textures to borrow
155
if let Some(view_textures) = view_textures.iter().next() {
156
commands.entity(probe).insert(AtmosphereProbeTextures {
157
environment: environment_view,
158
transmittance_lut: view_textures.transmittance_lut.clone(),
159
multiscattering_lut: view_textures.multiscattering_lut.clone(),
160
sky_view_lut: view_textures.sky_view_lut.clone(),
161
aerial_view_lut: view_textures.aerial_view_lut.clone(),
162
});
163
}
164
}
165
}
166
167
pub fn init_atmosphere_probe_pipeline(
168
pipeline_cache: Res<PipelineCache>,
169
layouts: Res<AtmosphereProbeLayouts>,
170
asset_server: Res<AssetServer>,
171
mut commands: Commands,
172
) {
173
let environment = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
174
label: Some("environment_pipeline".into()),
175
layout: vec![layouts.environment.clone()],
176
shader: load_embedded_asset!(asset_server.as_ref(), "environment.wgsl"),
177
..default()
178
});
179
commands.insert_resource(AtmosphereProbePipeline { environment });
180
}
181
182
// Ensure power-of-two dimensions to avoid edge update issues on cubemap faces
183
pub fn validate_environment_map_size(size: UVec2) -> UVec2 {
184
let new_size = UVec2::new(
185
size.x.max(1).next_power_of_two(),
186
size.y.max(1).next_power_of_two(),
187
);
188
if new_size != size {
189
warn!(
190
"Non-power-of-two AtmosphereEnvironmentMapLight size {}, correcting to {new_size}",
191
size
192
);
193
}
194
new_size
195
}
196
197
pub fn prepare_atmosphere_probe_components(
198
probes: Query<(Entity, &AtmosphereEnvironmentMapLight), (Without<AtmosphereEnvironmentMap>,)>,
199
mut commands: Commands,
200
mut images: ResMut<Assets<Image>>,
201
) {
202
for (entity, env_map_light) in &probes {
203
// Create a cubemap image in the main world that we can reference
204
let new_size = validate_environment_map_size(env_map_light.size);
205
let mut environment_image = Image::new_fill(
206
Extent3d {
207
width: new_size.x,
208
height: new_size.y,
209
depth_or_array_layers: 6,
210
},
211
TextureDimension::D2,
212
&[0; 8],
213
TextureFormat::Rgba16Float,
214
RenderAssetUsages::all(),
215
);
216
217
environment_image.texture_view_descriptor = Some(TextureViewDescriptor {
218
dimension: Some(TextureViewDimension::Cube),
219
..Default::default()
220
});
221
222
environment_image.texture_descriptor.usage = TextureUsages::TEXTURE_BINDING
223
| TextureUsages::STORAGE_BINDING
224
| TextureUsages::COPY_SRC;
225
226
// Add the image to assets to get a handle
227
let environment_handle = images.add(environment_image);
228
229
commands.entity(entity).insert(AtmosphereEnvironmentMap {
230
environment_map: environment_handle.clone(),
231
size: new_size,
232
});
233
234
commands
235
.entity(entity)
236
.insert(GeneratedEnvironmentMapLight {
237
environment_map: environment_handle,
238
intensity: env_map_light.intensity,
239
rotation: Quat::IDENTITY,
240
affects_lightmapped_mesh_diffuse: env_map_light.affects_lightmapped_mesh_diffuse,
241
});
242
}
243
}
244
pub fn atmosphere_environment(
245
view: ViewQuery<(
246
&DynamicUniformIndex<GpuAtmosphere>,
247
&DynamicUniformIndex<GpuAtmosphereSettings>,
248
&AtmosphereTransformsOffset,
249
&ViewUniformOffset,
250
&ViewLightsUniformOffset,
251
)>,
252
probe_query: Query<(&AtmosphereProbeBindGroups, &AtmosphereEnvironmentMap)>,
253
pipeline_cache: Res<PipelineCache>,
254
pipelines: Res<AtmosphereProbePipeline>,
255
mut ctx: RenderContext,
256
) {
257
let Some(environment_pipeline) = pipeline_cache.get_compute_pipeline(pipelines.environment)
258
else {
259
return;
260
};
261
262
let (
263
atmosphere_uniforms_offset,
264
settings_uniforms_offset,
265
atmosphere_transforms_offset,
266
view_uniforms_offset,
267
lights_uniforms_offset,
268
) = view.into_inner();
269
270
for (bind_groups, env_map_light) in probe_query.iter() {
271
let command_encoder = ctx.command_encoder();
272
let mut pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
273
label: Some("environment_pass"),
274
timestamp_writes: None,
275
});
276
277
pass.set_pipeline(environment_pipeline);
278
pass.set_bind_group(
279
0,
280
&bind_groups.environment,
281
&[
282
atmosphere_uniforms_offset.index(),
283
settings_uniforms_offset.index(),
284
atmosphere_transforms_offset.index(),
285
view_uniforms_offset.offset,
286
lights_uniforms_offset.offset,
287
],
288
);
289
290
pass.dispatch_workgroups(
291
env_map_light.size.x / 8,
292
env_map_light.size.y / 8,
293
6, // 6 cubemap faces
294
);
295
}
296
}
297
298