-
Notifications
You must be signed in to change notification settings - Fork 0
/
sky.cpp
201 lines (160 loc) · 5.29 KB
/
sky.cpp
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include "sky.h"
#include <cmath>
#include <eigen3/Eigen/Dense>
namespace construct {
Sky::Sky() {
turbidity = 10;
// Initialize the Sun.
sun_direction = Eigen::Vector3f(0, 20, 1);
sun_direction.normalize();
sun_power = Colorf(150e3, 150e3, 150e3); // lx
}
std::shared_ptr<Texture> Sky::generateEquirectangular() {
const int height = 256;
const int width = height * 2;
auto texture = Texture::create(width, height, true);
std::vector<float> data(width * height * 3, 0);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
const float theta = pi * static_cast<float>(y) / height;
const float phi = 2 * pi * static_cast<float>(x) / width;
const Colorf radiance = getRadianceAt(theta, phi, false);
for(int channel = 0; channel < 3; channel++) {
data[(y * width + x) * 3 + channel] = radiance[channel];
}
}
}
texture->useIn();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_FLOAT, data.data());
return texture;
}
Colorf Sky::getRadianceAt(Eigen::Vector3f dir, bool checkerboard) {
float theta = std::acos(dir.z());
float phi = std::atan2(dir.y(), dir.x());
return getRadianceAt(theta, phi, checkerboard);
}
Colorf Sky::getRadianceAt(float theta, float phi, bool checkerboard) {
if(checkerboard) {
const int x = theta / (pi / 5);
const int y = phi / (pi / 5);
const int parity = (x ^ y) % 2;
const float val = parity ? 150 : 100;
return Colorf(val, val, val);
}
if(theta > pi / 2) {
return Colorf(0, 0, 0);
}
// Preetham sky model
// http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf
//
// Hints for reading the paper:
// * we don't consider object light scattering
// (maybe needed later when considering far-away moutains or such)
// * when there's no object, L(0) = 0 (universe background)
// * don't get distracted by approximations for hand-calculation
// (it's simpler (and more flexible) to do numerical calc + smart memoization)
const float view_height = 0;
const float alpha_haze = 0.8333; // haze: /km
const float alpha_mole = 0.1136; // molecules: /km
const Eigen::Vector3f view_direction(
std::sin(theta) * std::cos(phi),
std::sin(theta) * std::sin(phi),
std::cos(theta));
// Based on Nishita's 1st-order scattering sky model.
// We ignore point-to-space decay.
Colorf radiance(0, 0, 0);
Colorf decay_here_to_view(1, 1, 1);
for(int i = 0; i < 50; i++) {
const float distance = i;
const float height = distance * std::cos(theta) + view_height;
//
const float cos_view_sun = view_direction.dot(sun_direction);
// Calculate scattering
// Mie scattering's phase function is not well-described anywhere.
// But it's said to be very sharp. So
// we use (1+cos^3 theta)^2/4
// (http://www.wolframalpha.com/input/?i=plot+r%3D%281%2Bcos%5E3+theta%29%5E2%2F4)
const Colorf scatter =
particleDensity(alpha_mole, distance, theta) * rayleigh(cos_view_sun) +
particleDensity(alpha_haze, distance, theta) * mie(cos_view_sun);
// Note. decay_space_to_here and decay_here_to_view are not colinear.
radiance += sun_power
.cwiseProduct(scatter)
.cwiseProduct(decay_here_to_view);
const Colorf decay_scatter =
particleDensity(alpha_mole, distance, theta) * rayleighTotal() +
particleDensity(alpha_haze, distance, theta) * mieTotal();
decay_here_to_view = decay_here_to_view.cwiseProduct(
Colorf(1, 1, 1) - decay_scatter);
}
assert(radiance[0] >= 0 && radiance[1] >= 0 && radiance[2] >= 0);
radiance /= 100;
return radiance;
}
Colorf Sky::rayleighTotal() {
return Colorf(
rayleighTotal(wl_r),
rayleighTotal(wl_g),
rayleighTotal(wl_b));
}
Colorf Sky::mieTotal() {
return Colorf(
mieTotal(wl_r),
mieTotal(wl_g),
mieTotal(wl_b));
}
float Sky::rayleighTotal(float lambda) {
const float n_minus_1 = 0.00003;
const float N = 2.545e25;
const float pn = 0.035;
return
8 * std::pow(pi, 3) * std::pow(2 * n_minus_1, 2) /
(3 * N * std::pow(lambda, 4)) *
((6 + 3 * pn) / (6 - 7 * pn));
}
float Sky::mieTotal(float lambda) {
assert(1 <= turbidity);
const float n = 1.00003;
const float c = (0.6544 * turbidity - 0.6510) * 1e-16;
const float junge_exponent = 4;
return 0.434 * c * std::pow(2 * pi / lambda, junge_exponent - 2) * 0.5 *
0.67;
}
Colorf Sky::rayleigh(float cos) {
return Colorf(
rayleigh(cos, wl_r),
rayleigh(cos, wl_g),
rayleigh(cos, wl_b));
}
Colorf Sky::mie(float cos) {
return Colorf(
mie(cos, wl_r),
mie(cos, wl_g),
mie(cos, wl_b));
}
// Taken from Preetham, Appendix.3
float Sky::rayleigh(float cos, float lambda) {
const float n_minus_1 = 0.00003;
const float N = 2.545e25;
const float pn = 0.035;
return
std::pow(pi * (2 * n_minus_1), 2) / (2 * N * std::pow(lambda, 4)) *
((6 + 3 * pn) / (6 - 7 * pn)) *
(1 + std::pow(cos, 2));
}
// Taken from Preetham, Appendix.3 (Wavelength-dependent component is
// approximated by hand)
float Sky::mie(float cos, float lambda) {
assert(1 <= turbidity);
const float n = 1.00003;
const float c = (0.6544 * turbidity - 0.6510) * 1e-16;
const float junge_exponent = 4;
return 0.434 * c * std::pow(2 * pi / lambda, junge_exponent - 2) * 0.5 *
std::pow(1 + std::pow(cos, 3), 2);
}
// u(distance) in the paper.
float Sky::particleDensity(float alpha, float distance, float theta) {
const float view_height = 0;
return std::exp(- alpha * (view_height + distance * std::cos(theta)));
}
} // namespace