1use crate::core::*;
2use crate::renderer::*;
3
4#[derive(Debug, Copy, Clone, PartialEq)]
8pub struct EnvironmentOptions {
9 pub lighting_model: LightingModel,
11 pub irradiance_map_size: u32,
13 pub irradiance_sample_count: u32,
15 pub prefilter_map_size: u32,
17 pub prefilter_map_max_mip_levels: u32,
19 pub prefilter_sample_count: u32,
21 pub brdf_map_size: u32,
23 pub brdf_sample_count: u32,
25}
26
27impl Default for EnvironmentOptions {
28 fn default() -> Self {
29 Self {
30 lighting_model: LightingModel::Cook(
31 NormalDistributionFunction::TrowbridgeReitzGGX,
32 GeometryFunction::SmithSchlickGGX,
33 ),
34 irradiance_map_size: 32,
35 irradiance_sample_count: 256,
36 prefilter_map_size: 128,
37 prefilter_map_max_mip_levels: 5,
38 prefilter_sample_count: 256,
39 brdf_map_size: 512,
40 brdf_sample_count: 128,
41 }
42 }
43}
44
45pub struct Environment {
50 pub irradiance_map: TextureCubeMap,
52 pub prefilter_map: TextureCubeMap,
55 pub brdf_map: Texture2D,
57}
58
59impl Environment {
60 pub fn new(context: &Context, environment_map: &TextureCubeMap) -> Self {
65 Self::new_with_options(context, environment_map, EnvironmentOptions::default())
66 }
67
68 pub fn new_with_lighting_model(
73 context: &Context,
74 environment_map: &TextureCubeMap,
75 lighting_model: LightingModel,
76 ) -> Self {
77 Self::new_with_options(
78 context,
79 environment_map,
80 EnvironmentOptions {
81 lighting_model,
82 ..Default::default()
83 },
84 )
85 }
86
87 pub fn new_with_options(
92 context: &Context,
93 environment_map: &TextureCubeMap,
94 options: EnvironmentOptions,
95 ) -> Self {
96 let irradiance_map = TextureCubeMap::new_empty::<[f16; 4]>(
98 context,
99 options.irradiance_map_size,
100 options.irradiance_map_size,
101 Interpolation::Linear,
102 Interpolation::Linear,
103 Some(Mipmap::default()),
104 Wrapping::ClampToEdge,
105 Wrapping::ClampToEdge,
106 Wrapping::ClampToEdge,
107 );
108 {
109 let viewport = Viewport::new_at_origo(irradiance_map.width(), irradiance_map.height());
110 for side in CubeMapSide::iter() {
111 irradiance_map
112 .as_color_target(&[side], None)
113 .clear(ClearState::default())
114 .apply_screen_material(
115 &IrradianceMaterial {
116 environment_map,
117 side,
118 sample_count: options.irradiance_sample_count,
119 },
120 Camera::new_2d(viewport),
121 &[],
122 );
123 }
124 }
125
126 let prefilter_map = TextureCubeMap::new_empty::<[f16; 4]>(
128 context,
129 options.prefilter_map_size,
130 options.prefilter_map_size,
131 Interpolation::Linear,
132 Interpolation::Linear,
133 Some(Mipmap::default()),
134 Wrapping::ClampToEdge,
135 Wrapping::ClampToEdge,
136 Wrapping::ClampToEdge,
137 );
138 {
139 for mip in 0..options.prefilter_map_max_mip_levels {
140 for side in CubeMapSide::iter() {
141 let sides = [side];
142 let color_target = prefilter_map.as_color_target(&sides, Some(mip));
143 let viewport =
144 Viewport::new_at_origo(color_target.width(), color_target.height());
145 color_target
146 .clear(ClearState::default())
147 .apply_screen_material(
148 &PrefilterMaterial {
149 lighting_model: options.lighting_model,
150 environment_map,
151 side,
152 mip,
153 max_mip_levels: options.prefilter_map_max_mip_levels,
154 sample_count: options.prefilter_sample_count,
155 },
156 Camera::new_2d(viewport),
157 &[],
158 );
159 }
160 }
161 }
162
163 let brdf_map = Texture2D::new_empty::<[f32; 2]>(
165 context,
166 options.brdf_map_size,
167 options.brdf_map_size,
168 Interpolation::Linear,
169 Interpolation::Linear,
170 None,
171 Wrapping::ClampToEdge,
172 Wrapping::ClampToEdge,
173 );
174 let viewport = Viewport::new_at_origo(brdf_map.width(), brdf_map.height());
175 brdf_map
176 .as_color_target(None)
177 .clear(ClearState::default())
178 .apply_screen_material(
179 &BrdfMaterial {
180 lighting_model: options.lighting_model,
181 sample_count: options.brdf_sample_count,
182 },
183 Camera::new_2d(viewport),
184 &[],
185 );
186
187 Self {
188 irradiance_map,
189 prefilter_map,
190 brdf_map,
191 }
192 }
193}
194
195struct PrefilterMaterial<'a> {
196 lighting_model: LightingModel,
197 environment_map: &'a TextureCubeMap,
198 side: CubeMapSide,
199 mip: u32,
200 max_mip_levels: u32,
201 sample_count: u32,
202}
203
204impl Material for PrefilterMaterial<'_> {
205 fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
206 format!(
207 "{}{}{}",
208 include_str!("../../core/shared.frag"),
209 include_str!("shaders/light_shared.frag"),
210 include_str!("shaders/prefilter.frag")
211 )
212 }
213
214 fn id(&self) -> EffectMaterialId {
215 EffectMaterialId::PrefilterMaterial
216 }
217
218 fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
219 program.use_uniform_if_required("lightingModel", lighting_model_to_id(self.lighting_model));
220 program.use_texture_cube("environmentMap", self.environment_map);
221 program.use_uniform(
222 "roughness",
223 self.mip as f32 / (self.max_mip_levels as f32 - 1.0),
224 );
225 program.use_uniform("resolution", self.environment_map.width() as f32);
226 program.use_uniform("direction", self.side.direction());
227 program.use_uniform("up", self.side.up());
228 program.use_uniform("sampleCount", self.sample_count);
229 }
230
231 fn render_states(&self) -> RenderStates {
232 RenderStates::default()
233 }
234
235 fn material_type(&self) -> MaterialType {
236 MaterialType::Opaque
237 }
238}
239
240struct BrdfMaterial {
241 lighting_model: LightingModel,
242 sample_count: u32,
243}
244
245impl Material for BrdfMaterial {
246 fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
247 format!(
248 "{}{}{}",
249 include_str!("../../core/shared.frag"),
250 include_str!("shaders/light_shared.frag"),
251 include_str!("shaders/brdf.frag")
252 )
253 }
254
255 fn id(&self) -> EffectMaterialId {
256 EffectMaterialId::BrdfMaterial
257 }
258
259 fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
260 program.use_uniform_if_required("lightingModel", lighting_model_to_id(self.lighting_model));
261 program.use_uniform("sampleCount", self.sample_count);
262 }
263
264 fn render_states(&self) -> RenderStates {
265 RenderStates::default()
266 }
267
268 fn material_type(&self) -> MaterialType {
269 MaterialType::Opaque
270 }
271}
272
273struct IrradianceMaterial<'a> {
274 environment_map: &'a TextureCubeMap,
275 side: CubeMapSide,
276 sample_count: u32,
277}
278
279impl Material for IrradianceMaterial<'_> {
280 fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
281 format!(
282 "{}{}",
283 include_str!("../../core/shared.frag"),
284 include_str!("shaders/irradiance.frag")
285 )
286 }
287
288 fn id(&self) -> EffectMaterialId {
289 EffectMaterialId::IrradianceMaterial
290 }
291
292 fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
293 program.use_texture_cube("environmentMap", self.environment_map);
294 program.use_uniform("direction", self.side.direction());
295 program.use_uniform("up", self.side.up());
296 program.use_uniform("sampleCount", self.sample_count);
297 }
298
299 fn render_states(&self) -> RenderStates {
300 RenderStates::default()
301 }
302
303 fn material_type(&self) -> MaterialType {
304 MaterialType::Opaque
305 }
306}