1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::core::*;
use crate::renderer::*;
#[allow(missing_docs)]
pub struct Environment {
pub irradiance_map: TextureCubeMap<f16>,
pub prefilter_map: TextureCubeMap<f16>,
pub brdf_map: Texture2D<f16>,
}
impl Environment {
pub fn new(context: &Context, environment_map: &impl TextureCube) -> ThreeDResult<Self> {
let lighting_model = LightingModel::Cook(
NormalDistributionFunction::TrowbridgeReitzGGX,
GeometryFunction::SmithSchlickGGX,
);
let irradiance_size = 32;
let mut irradiance_map = TextureCubeMap::new_empty(
context,
irradiance_size,
irradiance_size,
Interpolation::Linear,
Interpolation::Linear,
Some(Interpolation::Linear),
Wrapping::ClampToEdge,
Wrapping::ClampToEdge,
Wrapping::ClampToEdge,
Format::RGBA,
)?;
{
let fragment_shader_source = format!(
"{}{}",
include_str!("../../core/shared.frag"),
include_str!("shaders/irradiance.frag")
);
let effect = ImageCubeEffect::new(context, &fragment_shader_source)?;
let render_target = RenderTargetCubeMap::new_color(context, &mut irradiance_map)?;
for side in CubeMapSide::iter() {
effect.use_texture_cube("environmentMap", environment_map)?;
let viewport = Viewport::new_at_origo(irradiance_size, irradiance_size);
render_target.write(side, ClearState::default(), || {
effect.render(side, RenderStates::default(), viewport)
})?;
}
}
let prefilter_size = 128;
let mut prefilter_map = TextureCubeMap::new_empty(
context,
prefilter_size,
prefilter_size,
Interpolation::Linear,
Interpolation::Linear,
Some(Interpolation::Linear),
Wrapping::ClampToEdge,
Wrapping::ClampToEdge,
Wrapping::ClampToEdge,
Format::RGBA,
)?;
{
let fragment_shader_source = format!(
"{}{}{}{}",
lighting_model.shader(),
include_str!("../../core/shared.frag"),
include_str!("shaders/light_shared.frag"),
include_str!("shaders/prefilter.frag")
);
let program = ImageCubeEffect::new(context, &fragment_shader_source)?;
let render_target = RenderTargetCubeMap::new_color(context, &mut prefilter_map)?;
let max_mip_levels = 5;
for mip in 0..max_mip_levels {
let roughness = mip as f32 / (max_mip_levels as f32 - 1.0);
let viewport = Viewport::new_at_origo(
prefilter_size / 2u32.pow(mip),
prefilter_size / 2u32.pow(mip),
);
for side in CubeMapSide::iter() {
program.use_texture_cube("environmentMap", environment_map)?;
program.use_uniform_float("roughness", &roughness)?;
program.use_uniform_float("resolution", &(environment_map.width() as f32))?;
render_target.write_to_mip_level(side, mip, ClearState::default(), || {
program.render(side, RenderStates::default(), viewport)
})?;
}
}
}
let mut brdf_map = Texture2D::new_empty(
context,
512,
512,
Interpolation::Linear,
Interpolation::Linear,
None,
Wrapping::ClampToEdge,
Wrapping::ClampToEdge,
Format::RG,
)?;
let effect = ImageEffect::new(
context,
&format!(
"{}{}{}{}",
lighting_model.shader(),
include_str!("../../core/shared.frag"),
include_str!("shaders/light_shared.frag"),
include_str!("shaders/brdf.frag")
),
)?;
let viewport = Viewport::new_at_origo(brdf_map.width(), brdf_map.height());
brdf_map.write(ClearState::default(), || {
effect.apply(RenderStates::default(), viewport)
})?;
Ok(Self {
irradiance_map,
prefilter_map,
brdf_map,
})
}
}