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