1use crate::core::*;
2use crate::renderer::*;
3
4pub struct Environment {
9 pub irradiance_map: TextureCubeMap,
11 pub prefilter_map: TextureCubeMap,
14 pub brdf_map: Texture2D,
16}
17
18impl Environment {
19 pub fn new(context: &Context, environment_map: &TextureCubeMap) -> Self {
24 Self::new_with_lighting_model(
25 context,
26 environment_map,
27 LightingModel::Cook(
28 NormalDistributionFunction::TrowbridgeReitzGGX,
29 GeometryFunction::SmithSchlickGGX,
30 ),
31 )
32 }
33
34 pub fn new_with_lighting_model(
38 context: &Context,
39 environment_map: &TextureCubeMap,
40 lighting_model: LightingModel,
41 ) -> Self {
42 let irradiance_size = 32;
44 let mut irradiance_map = TextureCubeMap::new_empty::<[f16; 4]>(
45 context,
46 irradiance_size,
47 irradiance_size,
48 Interpolation::Linear,
49 Interpolation::Linear,
50 Some(Mipmap::default()),
51 Wrapping::ClampToEdge,
52 Wrapping::ClampToEdge,
53 Wrapping::ClampToEdge,
54 );
55 {
56 let viewport = Viewport::new_at_origo(irradiance_size, irradiance_size);
57 for side in CubeMapSide::iter() {
58 irradiance_map
59 .as_color_target(&[side], None)
60 .clear(ClearState::default())
61 .apply_screen_material(
62 &IrradianceMaterial {
63 environment_map,
64 side,
65 },
66 &Camera::new_2d(viewport),
67 &[],
68 );
69 }
70 }
71
72 let prefilter_size = 128;
74 let mut prefilter_map = TextureCubeMap::new_empty::<[f16; 4]>(
75 context,
76 prefilter_size,
77 prefilter_size,
78 Interpolation::Linear,
79 Interpolation::Linear,
80 Some(Mipmap::default()),
81 Wrapping::ClampToEdge,
82 Wrapping::ClampToEdge,
83 Wrapping::ClampToEdge,
84 );
85 {
86 let max_mip_levels = 5;
87 for mip in 0..max_mip_levels {
88 for side in CubeMapSide::iter() {
89 let sides = [side];
90 let color_target = prefilter_map.as_color_target(&sides, Some(mip));
91 let viewport =
92 Viewport::new_at_origo(color_target.width(), color_target.height());
93 color_target
94 .clear(ClearState::default())
95 .apply_screen_material(
96 &PrefilterMaterial {
97 lighting_model,
98 environment_map,
99 side,
100 mip,
101 max_mip_levels,
102 },
103 &Camera::new_2d(viewport),
104 &[],
105 );
106 }
107 }
108 }
109
110 let mut brdf_map = Texture2D::new_empty::<[f32; 2]>(
112 context,
113 512,
114 512,
115 Interpolation::Linear,
116 Interpolation::Linear,
117 None,
118 Wrapping::ClampToEdge,
119 Wrapping::ClampToEdge,
120 );
121 let viewport = Viewport::new_at_origo(brdf_map.width(), brdf_map.height());
122 brdf_map
123 .as_color_target(None)
124 .clear(ClearState::default())
125 .apply_screen_material(
126 &BrdfMaterial { lighting_model },
127 &Camera::new_2d(viewport),
128 &[],
129 );
130
131 Self {
132 irradiance_map,
133 prefilter_map,
134 brdf_map,
135 }
136 }
137}
138
139struct PrefilterMaterial<'a> {
140 lighting_model: LightingModel,
141 environment_map: &'a TextureCubeMap,
142 side: CubeMapSide,
143 mip: u32,
144 max_mip_levels: u32,
145}
146
147impl Material for PrefilterMaterial<'_> {
148 fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
149 format!(
150 "{}{}{}",
151 include_str!("../../core/shared.frag"),
152 include_str!("shaders/light_shared.frag"),
153 include_str!("shaders/prefilter.frag")
154 )
155 }
156
157 fn id(&self) -> EffectMaterialId {
158 EffectMaterialId::PrefilterMaterial
159 }
160
161 fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
162 program.use_uniform_if_required("lightingModel", lighting_model_to_id(self.lighting_model));
163 program.use_texture_cube("environmentMap", self.environment_map);
164 program.use_uniform(
165 "roughness",
166 self.mip as f32 / (self.max_mip_levels as f32 - 1.0),
167 );
168 program.use_uniform("resolution", self.environment_map.width() as f32);
169 program.use_uniform("direction", self.side.direction());
170 program.use_uniform("up", self.side.up());
171 }
172
173 fn render_states(&self) -> RenderStates {
174 RenderStates::default()
175 }
176
177 fn material_type(&self) -> MaterialType {
178 MaterialType::Opaque
179 }
180}
181
182struct BrdfMaterial {
183 lighting_model: LightingModel,
184}
185
186impl Material for BrdfMaterial {
187 fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
188 format!(
189 "{}{}{}",
190 include_str!("../../core/shared.frag"),
191 include_str!("shaders/light_shared.frag"),
192 include_str!("shaders/brdf.frag")
193 )
194 }
195
196 fn id(&self) -> EffectMaterialId {
197 EffectMaterialId::BrdfMaterial
198 }
199
200 fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
201 program.use_uniform_if_required("lightingModel", lighting_model_to_id(self.lighting_model));
202 }
203
204 fn render_states(&self) -> RenderStates {
205 RenderStates::default()
206 }
207
208 fn material_type(&self) -> MaterialType {
209 MaterialType::Opaque
210 }
211}
212
213struct IrradianceMaterial<'a> {
214 environment_map: &'a TextureCubeMap,
215 side: CubeMapSide,
216}
217
218impl Material for IrradianceMaterial<'_> {
219 fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
220 format!(
221 "{}{}",
222 include_str!("../../core/shared.frag"),
223 include_str!("shaders/irradiance.frag")
224 )
225 }
226
227 fn id(&self) -> EffectMaterialId {
228 EffectMaterialId::IrradianceMaterial
229 }
230
231 fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
232 program.use_texture_cube("environmentMap", self.environment_map);
233 program.use_uniform("direction", self.side.direction());
234 program.use_uniform("up", self.side.up());
235 }
236
237 fn render_states(&self) -> RenderStates {
238 RenderStates::default()
239 }
240
241 fn material_type(&self) -> MaterialType {
242 MaterialType::Opaque
243 }
244}