three_d/renderer/light/
environment.rs

1use crate::core::*;
2use crate::renderer::*;
3
4///
5/// Precalculations of light shining from an environment map (known as image based lighting - IBL).
6/// This allows for real-time rendering of ambient light from the environment (see [AmbientLight](crate::AmbientLight)).
7///
8pub struct Environment {
9    /// A cube map used to calculate the diffuse contribution from the environment.
10    pub irradiance_map: TextureCubeMap,
11    /// A cube map used to calculate the specular contribution from the environment.
12    /// Each mip-map level contain the prefiltered color for a certain surface roughness.
13    pub prefilter_map: TextureCubeMap,
14    /// A 2D texture that contain the BRDF lookup tables (LUT).
15    pub brdf_map: Texture2D,
16}
17
18impl Environment {
19    ///
20    /// Computes the maps needed for physically based rendering with lighting from an environment from the given environment map.
21    /// A default Cook-Torrance lighting model is used.
22    ///
23    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    ///
35    /// Computes the maps needed for physically based rendering with lighting from an environment from the given environment map and with the specified lighting model.
36    ///
37    pub fn new_with_lighting_model(
38        context: &Context,
39        environment_map: &TextureCubeMap,
40        lighting_model: LightingModel,
41    ) -> Self {
42        // Diffuse
43        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        // Prefilter
73        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        // BRDF
111        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}