Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/medium.rs
9351 views
1
use bevy_light::atmosphere::{ScatteringMedium, ScatteringTerm};
2
3
use bevy_app::{App, Plugin};
4
use bevy_asset::AssetId;
5
use bevy_ecs::{
6
resource::Resource,
7
system::{Commands, Res, SystemParamItem},
8
};
9
use bevy_math::Vec4;
10
use bevy_render::{
11
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin},
12
render_resource::{
13
Extent3d, FilterMode, Sampler, SamplerDescriptor, Texture, TextureDataOrder,
14
TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
15
TextureViewDescriptor,
16
},
17
renderer::{RenderDevice, RenderQueue},
18
RenderApp, RenderStartup,
19
};
20
use smallvec::SmallVec;
21
22
#[doc(hidden)]
23
pub struct ScatteringMediumPlugin;
24
25
impl Plugin for ScatteringMediumPlugin {
26
fn build(&self, app: &mut App) {
27
app.add_plugins(RenderAssetPlugin::<GpuScatteringMedium>::default());
28
29
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
30
render_app.add_systems(RenderStartup, init_scattering_medium_sampler);
31
}
32
}
33
}
34
35
/// The GPU representation of a [`ScatteringMedium`].
36
pub struct GpuScatteringMedium {
37
/// The terms of the scattering medium.
38
pub terms: SmallVec<[ScatteringTerm; 1]>,
39
/// The resolution at which to sample the falloff distribution of each
40
/// scattering term.
41
pub falloff_resolution: u32,
42
/// The resolution at which to sample the phase function of each
43
/// scattering term.
44
pub phase_resolution: u32,
45
/// The `density_lut`, a 2D `falloff_resolution x 2` LUT which contains the
46
/// medium's optical density with respect to the atmosphere's "falloff parameter",
47
/// a linear value which is 1.0 at the planet's surface and 0.0 at the edge of
48
/// space. The first and second rows correspond to absorption density and
49
/// scattering density respectively.
50
pub density_lut: Texture,
51
/// The default [`TextureView`] of the `density_lut`
52
pub density_lut_view: TextureView,
53
/// The `scattering_lut`, a 2D `falloff_resolution x phase_resolution` LUT which
54
/// contains the medium's scattering density multiplied by the phase function, with
55
/// the U axis corresponding to the falloff parameter and the V axis corresponding
56
/// to `neg_LdotV * 0.5 + 0.5`, where `neg_LdotV` is the dot product of the light
57
/// direction and the incoming view vector.
58
pub scattering_lut: Texture,
59
/// The default [`TextureView`] of the `scattering_lut`
60
pub scattering_lut_view: TextureView,
61
}
62
63
impl RenderAsset for GpuScatteringMedium {
64
type SourceAsset = ScatteringMedium;
65
66
type Param = (Res<'static, RenderDevice>, Res<'static, RenderQueue>);
67
68
fn prepare_asset(
69
source_asset: Self::SourceAsset,
70
_asset_id: AssetId<Self::SourceAsset>,
71
(render_device, render_queue): &mut SystemParamItem<Self::Param>,
72
_previous_asset: Option<&Self>,
73
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
74
let mut density: Vec<Vec4> =
75
Vec::with_capacity(2 * source_asset.falloff_resolution as usize);
76
77
density.extend((0..source_asset.falloff_resolution).map(|i| {
78
let falloff = (i as f32 + 0.5) / source_asset.falloff_resolution as f32;
79
80
source_asset
81
.terms
82
.iter()
83
.map(|term| term.absorption.extend(0.0) * term.falloff.sample(falloff))
84
.sum::<Vec4>()
85
}));
86
87
density.extend((0..source_asset.falloff_resolution).map(|i| {
88
let falloff = (i as f32 + 0.5) / source_asset.falloff_resolution as f32;
89
90
source_asset
91
.terms
92
.iter()
93
.map(|term| term.scattering.extend(0.0) * term.falloff.sample(falloff))
94
.sum::<Vec4>()
95
}));
96
97
let mut scattering: Vec<Vec4> = Vec::with_capacity(
98
source_asset.falloff_resolution as usize * source_asset.phase_resolution as usize,
99
);
100
101
scattering.extend(
102
(0..source_asset.falloff_resolution * source_asset.phase_resolution).map(|raw_i| {
103
let i = raw_i % source_asset.phase_resolution;
104
let j = raw_i / source_asset.phase_resolution;
105
let falloff = (i as f32 + 0.5) / source_asset.falloff_resolution as f32;
106
let phase = (j as f32 + 0.5) / source_asset.phase_resolution as f32;
107
let neg_l_dot_v = phase * 2.0 - 1.0;
108
109
source_asset
110
.terms
111
.iter()
112
.map(|term| {
113
term.scattering.extend(0.0)
114
* term.falloff.sample(falloff)
115
* term.phase.sample(neg_l_dot_v)
116
})
117
.sum::<Vec4>()
118
}),
119
);
120
121
let density_lut = render_device.create_texture_with_data(
122
render_queue,
123
&TextureDescriptor {
124
label: source_asset
125
.label
126
.as_deref()
127
.map(|label| format!("{}_density_lut", label))
128
.as_deref()
129
.or(Some("scattering_medium_density_lut")),
130
size: Extent3d {
131
width: source_asset.falloff_resolution,
132
height: 2,
133
depth_or_array_layers: 1,
134
},
135
mip_level_count: 1,
136
sample_count: 1,
137
dimension: TextureDimension::D2,
138
format: TextureFormat::Rgba32Float,
139
usage: TextureUsages::TEXTURE_BINDING,
140
view_formats: &[],
141
},
142
TextureDataOrder::LayerMajor,
143
bytemuck::cast_slice(density.as_slice()),
144
);
145
146
let density_lut_view = density_lut.create_view(&TextureViewDescriptor {
147
label: source_asset
148
.label
149
.as_deref()
150
.map(|label| format!("{}_density_lut_view", label))
151
.as_deref()
152
.or(Some("scattering_medium_density_lut_view")),
153
..Default::default()
154
});
155
156
let scattering_lut = render_device.create_texture_with_data(
157
render_queue,
158
&TextureDescriptor {
159
label: source_asset
160
.label
161
.as_deref()
162
.map(|label| format!("{}_scattering_lut", label))
163
.as_deref()
164
.or(Some("scattering_medium_scattering_lut")),
165
size: Extent3d {
166
width: source_asset.falloff_resolution,
167
height: source_asset.phase_resolution,
168
depth_or_array_layers: 1,
169
},
170
mip_level_count: 1,
171
sample_count: 1,
172
dimension: TextureDimension::D2,
173
format: TextureFormat::Rgba32Float,
174
usage: TextureUsages::TEXTURE_BINDING,
175
view_formats: &[],
176
},
177
TextureDataOrder::LayerMajor,
178
bytemuck::cast_slice(scattering.as_slice()),
179
);
180
181
let scattering_lut_view = scattering_lut.create_view(&TextureViewDescriptor {
182
label: source_asset
183
.label
184
.as_deref()
185
.map(|label| format!("{}_scattering_lut", label))
186
.as_deref()
187
.or(Some("scattering_medium_scattering_lut_view")),
188
..Default::default()
189
});
190
191
Ok(Self {
192
terms: source_asset.terms,
193
falloff_resolution: source_asset.falloff_resolution,
194
phase_resolution: source_asset.phase_resolution,
195
density_lut,
196
density_lut_view,
197
scattering_lut,
198
scattering_lut_view,
199
})
200
}
201
}
202
203
/// The default sampler for all scattering media LUTs.
204
///
205
/// Just a bilinear clamp-to-edge sampler, nothing fancy.
206
#[derive(Resource)]
207
pub struct ScatteringMediumSampler(Sampler);
208
209
impl ScatteringMediumSampler {
210
pub fn sampler(&self) -> &Sampler {
211
&self.0
212
}
213
}
214
215
fn init_scattering_medium_sampler(mut commands: Commands, render_device: Res<RenderDevice>) {
216
let sampler = render_device.create_sampler(&SamplerDescriptor {
217
label: Some("scattering_medium_sampler"),
218
mag_filter: FilterMode::Linear,
219
min_filter: FilterMode::Linear,
220
..Default::default()
221
});
222
223
commands.insert_resource(ScatteringMediumSampler(sampler));
224
}
225
226