1 |
#ifndef lint |
2 |
static const char RCSid[] = "$Id$"; |
3 |
#endif |
4 |
/* |
5 |
The Radiance Software License, Version 2.0 |
6 |
|
7 |
Radiance v5.4 Copyright (c) 1990 to 2022, The Regents of the University of |
8 |
California, through Lawrence Berkeley National Laboratory (subject to receipt |
9 |
of any required approvals from the U.S. Dept. of Energy). All rights reserved. |
10 |
|
11 |
Redistribution and use in source and binary forms, with or without |
12 |
modification, are permitted provided that the following conditions are met: |
13 |
|
14 |
(1) Redistributions of source code must retain the above copyright notice, |
15 |
this list of conditions and the following disclaimer. |
16 |
|
17 |
(2) Redistributions in binary form must reproduce the above copyright |
18 |
notice, this list of conditions and the following disclaimer in the |
19 |
documentation and/or other materials provided with the distribution. |
20 |
|
21 |
(3) Neither the name of the University of California, Lawrence Berkeley |
22 |
National Laboratory, U.S. Dept. of Energy nor the names of its contributors |
23 |
may be used to endorse or promote products derived from this software |
24 |
without specific prior written permission. |
25 |
|
26 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
27 |
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
28 |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
29 |
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
30 |
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
31 |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
32 |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
33 |
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
34 |
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
35 |
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
36 |
POSSIBILITY OF SUCH DAMAGE. |
37 |
|
38 |
You are under no obligation whatsoever to provide any bug fixes, patches, |
39 |
or upgrades to the features, functionality or performance of the source |
40 |
code ("Enhancements") to anyone; however, if you choose to make your |
41 |
Enhancements available either publicly, or directly to Lawrence Berkeley |
42 |
National Laboratory, without imposing a separate written license agreement |
43 |
for such Enhancements, then you hereby grant the following license: a |
44 |
non-exclusive, royalty-free perpetual license to install, use, modify, |
45 |
prepare derivative works, incorporate into other computer software, |
46 |
distribute, and sublicense such enhancements or derivative works thereof, |
47 |
in binary and source code form. |
48 |
|
49 |
================================================================================= |
50 |
|
51 |
Copyright (c) 2017 Eric Bruneton |
52 |
All rights reserved. |
53 |
|
54 |
Redistribution and use in source and binary forms, with or without |
55 |
modification, are permitted provided that the following conditions are met: |
56 |
|
57 |
1. Redistributions of source code must retain the above copyright notice, this |
58 |
list of conditions and the following disclaimer. |
59 |
|
60 |
2. Redistributions in binary form must reproduce the above copyright notice, |
61 |
this list of conditions and the following disclaimer in the documentation |
62 |
and/or other materials provided with the distribution. |
63 |
|
64 |
3. Neither the name of the copyright holder nor the names of its contributors |
65 |
may be used to endorse or promote products derived from this software without |
66 |
specific prior written permission. |
67 |
|
68 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
69 |
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
70 |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
71 |
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
72 |
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
73 |
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
74 |
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
75 |
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
76 |
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
77 |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
78 |
|
79 |
Additional modifications by T. Wang |
80 |
- N-dimensional data storage and lookup using Radiance data structure |
81 |
- Mie scattering coefficients precomputed using libRadTrans and interpolated at runtime |
82 |
- Translation to C from C++ with multi-threading |
83 |
*/ |
84 |
|
85 |
#include "atmos.h" |
86 |
#include "data.h" |
87 |
|
88 |
#if defined(_WIN32) || defined(_WIN64) |
89 |
#include <windows.h> |
90 |
#else |
91 |
#include <pthread.h> |
92 |
#endif |
93 |
|
94 |
#if defined(_WIN32) || defined(_WIN64) |
95 |
#define THREAD HANDLE |
96 |
#define CREATE_THREAD(thread, func, context) \ |
97 |
((*(thread) = CreateThread(NULL, 0, (func), \ |
98 |
(context), 0, NULL)) != NULL) |
99 |
#define THREAD_RETURN DWORD WINAPI |
100 |
#define THREAD_JOIN(thread) WaitForSingleObject(thread, INFINITE) |
101 |
#define THREAD_CLOSE(thread) CloseHandle(thread) |
102 |
#else |
103 |
#define THREAD pthread_t |
104 |
#define CREATE_THREAD(thread, func, context) \ |
105 |
(pthread_create((thread), NULL, (func), (context)) == 0) |
106 |
#define THREAD_RETURN void * |
107 |
#define THREAD_JOIN(thread) pthread_join(thread, NULL) |
108 |
#endif |
109 |
|
110 |
typedef struct { |
111 |
int start_l; |
112 |
int end_l; |
113 |
const Atmosphere *atmos; |
114 |
DATARRAY *tau_dp; |
115 |
DATARRAY *delta_rayleigh_scattering_dp; |
116 |
DATARRAY *delta_mie_scattering_dp; |
117 |
DATARRAY *scattering_dp; |
118 |
} Scat1Tdat; |
119 |
|
120 |
typedef struct { |
121 |
int start_l; |
122 |
int end_l; |
123 |
DATARRAY *tau_dp; |
124 |
DATARRAY *delta_scattering_density_dp; |
125 |
DATARRAY *delta_multiple_scattering_dp; |
126 |
DATARRAY *scattering_dp; |
127 |
} ScatNTdat; |
128 |
|
129 |
typedef struct { |
130 |
int start_l; |
131 |
int end_l; |
132 |
const Atmosphere *atmos; |
133 |
DATARRAY *tau_dp; |
134 |
DATARRAY *delta_rayleigh_scattering_dp; |
135 |
DATARRAY *delta_mie_scattering_dp; |
136 |
DATARRAY *delta_multiple_scattering_dp; |
137 |
DATARRAY *delta_irradiance_dp; |
138 |
int scattering_order; |
139 |
DATARRAY *delta_scattering_density_dp; |
140 |
} ScatDenseTdat; |
141 |
|
142 |
const double ER = 6360000.0; /* Earth radius in meters */ |
143 |
const double AR = 6420000; /* Atmosphere radius in meters */ |
144 |
const double AH = 60000; /* Atmosphere thickness in meters */ |
145 |
|
146 |
const float START_WVL = 390.0; |
147 |
const float END_WVL = 770.0; |
148 |
const double SUNRAD = 0.004625; |
149 |
|
150 |
/* The cosine of the maximum Sun zenith angle */ |
151 |
const double MU_S_MIN = -0.2; |
152 |
|
153 |
/* Scale heights (m) */ |
154 |
/* Rayleigh scattering */ |
155 |
const double HR_MS = 8820; /* Midlatitude summer */ |
156 |
const double HR_MW = 8100; /* Midlatitude winter */ |
157 |
const double HR_SS = 8550; /* Subarctic summer */ |
158 |
const double HR_SW = 7600; /* Subarctic winter */ |
159 |
const double HR_T = 9050; /* Tropical */ |
160 |
|
161 |
const double AOD0_CA = 0.115; /* Broadband AOD for continental average */ |
162 |
|
163 |
const double SOLOMG = 6.7967e-5; /* .533 apex angle */ |
164 |
const int WVLSPAN = 400; /* 380nm to 780nm */ |
165 |
|
166 |
/* Aerosol optical depth at 550nm for continental clean */ |
167 |
const double AOD_CC_550 = 0.05352; |
168 |
|
169 |
/** 380nm to 780nm at 20nm intervals **/ |
170 |
/* Extraterrestrial solar W/m^2/nm */ |
171 |
const float EXTSOL[NSSAMP] = {1.11099345, 1.75363199, 1.67126921, 1.97235667, |
172 |
2.01863015, 1.93612482, 1.87310758, 1.88792588, |
173 |
1.86274213, 1.84433248, 1.80370942, 1.73020456, |
174 |
1.67036654, 1.57482149, 1.53646383, 1.45515319, |
175 |
1.38207435, 1.3265141, 1.26648111, 1.20592043}; |
176 |
|
177 |
/* Rayleight scattering coefficients at sea level in m^-1 */ |
178 |
const float BR0_MS[NSSAMP] = { |
179 |
4.63889967e-05, 3.76703293e-05, 3.09193474e-05, 2.56223081e-05, |
180 |
2.14198405e-05, 1.80483398e-05, 1.53177709e-05, 1.30867298e-05, |
181 |
1.12487269e-05, 9.72370830e-06, 8.44932642e-06, 7.37769845e-06, |
182 |
6.47116653e-06, 5.70005327e-06, 5.04091603e-06, 4.47430240e-06, |
183 |
3.98549839e-06, 3.56178512e-06, 3.19293761e-06, 2.87072599e-06}; |
184 |
|
185 |
const float BR0_MW[NSSAMP] = { |
186 |
5.03857564e-05, 4.09159104e-05, 3.35832809e-05, 2.78298619e-05, |
187 |
2.32653203e-05, 1.96033395e-05, 1.66375116e-05, 1.42142496e-05, |
188 |
1.22178890e-05, 1.05614786e-05, 9.17729921e-06, 8.01334246e-06, |
189 |
7.02870602e-06, 6.19115557e-06, 5.47522872e-06, 4.85979708e-06, |
190 |
4.32887894e-06, 3.86865960e-06, 3.46803311e-06, 3.11806054e-06}; |
191 |
|
192 |
const float BR0_SS[NSSAMP] = { |
193 |
4.73789047e-05, 3.84741872e-05, 3.15791442e-05, 2.61690698e-05, |
194 |
2.18769246e-05, 1.84334785e-05, 1.56446412e-05, 1.33659912e-05, |
195 |
1.14887667e-05, 9.93120528e-06, 8.62962901e-06, 7.53513326e-06, |
196 |
6.60925660e-06, 5.82168833e-06, 5.14848557e-06, 4.56978082e-06, |
197 |
4.07054608e-06, 3.63779107e-06, 3.26107261e-06, 2.93198523e-06}; |
198 |
|
199 |
const float BR0_SW[NSSAMP] = { |
200 |
5.30623659e-05, 4.30894595e-05, 3.53673035e-05, 2.93082494e-05, |
201 |
2.45012287e-05, 2.06447149e-05, 1.75213353e-05, 1.49693438e-05, |
202 |
1.28669320e-05, 1.11225291e-05, 9.66481886e-06, 8.43902999e-06, |
203 |
7.40208736e-06, 6.52004427e-06, 5.76608570e-06, 5.11796089e-06, |
204 |
4.55883914e-06, 4.07417187e-06, 3.65226316e-06, 3.28369923e-06}; |
205 |
const float BR0_T[NSSAMP] = { |
206 |
4.55376661e-05, 3.69790036e-05, 3.03519157e-05, 2.51520876e-05, |
207 |
2.10267438e-05, 1.77171168e-05, 1.50366593e-05, 1.28465622e-05, |
208 |
1.10422904e-05, 9.54525887e-06, 8.29426444e-06, 7.24230298e-06, |
209 |
6.35240772e-06, 5.59544593e-06, 4.94840517e-06, 4.39219003e-06, |
210 |
3.91235655e-06, 3.49641927e-06, 3.13434084e-06, 2.81804244e-06}; |
211 |
|
212 |
/* Ozone extinction coefficients at sea level */ |
213 |
const float AO0[NSSAMP] = { |
214 |
3.5661864e-09, 1.4848362e-08, 4.5415674e-08, 1.2446184e-07, 2.6461576e-07, |
215 |
4.8451984e-07, 8.609148e-07, 1.480537e-06, 1.8809e-06, 2.5107328e-06, |
216 |
2.5263174e-06, 2.313507e-06, 1.727741e-06, 1.2027012e-06, 7.915902e-07, |
217 |
5.0639202e-07, 3.5285684e-07, 2.23021e-07, 1.7395638e-07, 1.5052574e-07}; |
218 |
|
219 |
const double MIE_G = 0.7; /* Mie eccentricity */ |
220 |
|
221 |
const int TRANSMITTANCE_TEXTURE_WIDTH = 256; |
222 |
const int TRANSMITTANCE_TEXTURE_HEIGHT = 64; |
223 |
|
224 |
/* Resolution halved by original impl */ |
225 |
const int SCATTERING_TEXTURE_R_SIZE = 16; |
226 |
const int SCATTERING_TEXTURE_MU_SIZE = 64; |
227 |
const int SCATTERING_TEXTURE_MU_S_SIZE = 16; |
228 |
const int SCATTERING_TEXTURE_NU_SIZE = 4; |
229 |
|
230 |
const int IRRADIANCE_TEXTURE_WIDTH = 64; |
231 |
const int IRRADIANCE_TEXTURE_HEIGHT = 16; |
232 |
|
233 |
static inline double clamp_cosine(double mu) { |
234 |
return fmax(-1.0, fmin(1.0, mu)); |
235 |
} |
236 |
|
237 |
static inline double clamp_distance(double d) { return fmax(d, 0.0); } |
238 |
|
239 |
static inline double clamp_radius(double r) { return fmax(ER, fmin(r, AR)); } |
240 |
|
241 |
static inline double safe_sqrt(double a) { return sqrt(fmax(a, 0.0)); } |
242 |
|
243 |
static inline double distance_to_space(const double radius, const double mu) { |
244 |
const double descriminant = radius * radius * (mu * mu - 1.0) + AR * AR; |
245 |
return clamp_distance(-radius * mu + safe_sqrt(descriminant)); |
246 |
} |
247 |
|
248 |
static inline double distance_to_earth(const double radius, const double mu) { |
249 |
const double descriminant = radius * radius * (mu * mu - 1.0) + ER * ER; |
250 |
return clamp_distance(-radius * mu - safe_sqrt(descriminant)); |
251 |
} |
252 |
|
253 |
static inline int ray_hits_ground(double radius, double mu) { |
254 |
return mu < 0.0 && radius * radius * (mu * mu - 1.0) + ER * ER >= 0.0; |
255 |
} |
256 |
|
257 |
static inline double get_layer_density(const DensityProfileLayer *layer, |
258 |
const double altitude) { |
259 |
double density = layer->exp_term * exp(layer->exp_scale * altitude) + |
260 |
layer->linear_term * altitude + layer->constant_term; |
261 |
return fmax(0.0, fmin(1.0, density)); |
262 |
} |
263 |
|
264 |
static inline double get_profile_density(const DensityProfile *profile, |
265 |
const double altitude) { |
266 |
if (altitude < profile->layers[0].width) { |
267 |
return get_layer_density(&(profile->layers[0]), altitude); |
268 |
} else { |
269 |
return get_layer_density(&(profile->layers[1]), altitude); |
270 |
} |
271 |
} |
272 |
|
273 |
static int compute_optical_length_to_space_mie(DATARRAY *mdp, const double radius, |
274 |
const double mu, double *result) { |
275 |
/* Number of intervals for the numerical integration. */ |
276 |
const int nsamp = 500; |
277 |
/* The integration step, i.e. the length of each integration interval. */ |
278 |
const double dx = distance_to_space(radius, mu) / nsamp; |
279 |
/* Integration loop. */ |
280 |
for (int i = 0; i <= nsamp; ++i) { |
281 |
double d_i = i * dx; |
282 |
/* Distance between the current sample point and the planet center. */ |
283 |
double r_i = sqrt(d_i * d_i + 2.0 * radius * mu * d_i + radius * radius); |
284 |
double pt[1] = {r_i - ER}; |
285 |
DATARRAY *mie = datavector(mdp, pt); |
286 |
double weight_i = i == 0 || i == nsamp ? 0.5 : 1.0; |
287 |
for (int j = 0; j < NSSAMP; ++j) { |
288 |
result[j] += mie->arr.d[j] * weight_i * dx; |
289 |
} |
290 |
free(mie); |
291 |
} |
292 |
return 1; |
293 |
} |
294 |
|
295 |
static double compute_optical_length_to_space(const DensityProfile *profile, |
296 |
const double radius, const double mu) { |
297 |
/* Number of intervals for the numerical integration. */ |
298 |
const int SAMPLE_COUNT = 500; |
299 |
/* The integration step, i.e. the length of each integration interval. */ |
300 |
const double dx = distance_to_space(radius, mu) / SAMPLE_COUNT; |
301 |
/* Integration loop. */ |
302 |
double result = 0.0; |
303 |
for (int i = 0; i <= SAMPLE_COUNT; ++i) { |
304 |
double d_i = i * dx; |
305 |
/* Distance between the current sample point and the planet center. */ |
306 |
double r_i = sqrt(d_i * d_i + 2.0 * radius * mu * d_i + radius * radius); |
307 |
/* Number density at the current sample point (divided by the number density */ |
308 |
/* at the bottom of the atmosphere, yielding a dimensionless number). */ |
309 |
double y_i = get_profile_density(profile, r_i - ER); |
310 |
/* Sample weight (from the trapezoidal rule). */ |
311 |
double weight_i = i == 0 || i == SAMPLE_COUNT ? 0.5 : 1.0; |
312 |
result += y_i * weight_i * dx; |
313 |
} |
314 |
return result; |
315 |
} |
316 |
|
317 |
static void compute_transmittance_to_space(const Atmosphere *atmos, |
318 |
const double radius, const double mu, |
319 |
float *result) { |
320 |
const double taur = |
321 |
compute_optical_length_to_space(&atmos->rayleigh_density, radius, mu); |
322 |
double taum[NSSAMP] = {0}; |
323 |
compute_optical_length_to_space_mie(atmos->beta_m, radius, mu, taum); |
324 |
const double tauo = |
325 |
compute_optical_length_to_space(&atmos->ozone_density, radius, mu); |
326 |
for (int i = 0; i < NSSAMP; ++i) { |
327 |
result[i] = exp(-(taur * atmos->beta_r0[i] + taum[i] * atmos->beta_scale + |
328 |
tauo * AO0[i])); |
329 |
} |
330 |
} |
331 |
|
332 |
static inline double get_texture_coord_from_unit_range(const double x, |
333 |
const int texture_size) { |
334 |
return 0.5 / (double)texture_size + x * (1.0 - 1.0 / (double)texture_size); |
335 |
} |
336 |
|
337 |
static inline double get_unit_range_from_texture_coord(const double u, |
338 |
const int texture_size) { |
339 |
return (u - 0.5 / (double)texture_size) / (1.0 - 1.0 / (double)texture_size); |
340 |
} |
341 |
|
342 |
static void to_transmittance_uv(const double radius, const double mu, double *u, |
343 |
double *v) { |
344 |
/* Distance to top atmosphere boundary for a horizontal ray at ground level. */ |
345 |
double H = sqrt(AR * AR - ER * ER); |
346 |
/* Distance to the horizon. */ |
347 |
double rho = safe_sqrt(radius * radius - ER * ER); |
348 |
/* Distance to the top atmosphere boundary for the ray (r,mu), and its minimum */ |
349 |
/* and maximum values over all mu - obtained for (r,1) and (r,mu_horizon). */ |
350 |
double d = distance_to_space(radius, mu); |
351 |
double d_min = AR - radius; |
352 |
double d_max = rho + H; |
353 |
double x_mu = (d - d_min) / (d_max - d_min); |
354 |
double x_r = rho / H; |
355 |
*u = fmax(0.0, fmin(1.0, x_mu)); |
356 |
*v = fmax(0.0, fmin(1.0, x_r)); |
357 |
} |
358 |
|
359 |
static void from_transmittance_uv(const double u, const double v, double *radius, |
360 |
double *mu) { |
361 |
double x_mu = u; |
362 |
double x_r = v; |
363 |
/* Distance to top atmosphere boundary for a horizontal ray at ground level. */ |
364 |
double H = sqrt(AR * AR - ER * ER); |
365 |
/* Distance to the horizon, from which we can compute r: */ |
366 |
double rho = H * x_r; |
367 |
*radius = sqrt(rho * rho + ER * ER); |
368 |
/* Distance to the top atmosphere boundary for the ray (r,mu), and its minimum */ |
369 |
/* and maximum values over all mu - obtained for (r,1) and (r,mu_horizon) - */ |
370 |
/* from which we can recover mu: */ |
371 |
double d_min = AR - *radius; |
372 |
double d_max = rho + H; |
373 |
double d = d_min + x_mu * (d_max - d_min); |
374 |
*mu = d == 0.0 ? 1.0 : (H * H - rho * rho - d * d) / (2.0 * *radius * d); |
375 |
*mu = clamp_cosine(*mu); |
376 |
} |
377 |
|
378 |
static DATARRAY *get_transmittance_to_space(DATARRAY *dp, const double radius, |
379 |
const double mu) { |
380 |
double u, v; |
381 |
to_transmittance_uv(radius, mu, &u, &v); |
382 |
|
383 |
double pt[2] = {v, u}; |
384 |
return datavector(dp, pt); |
385 |
} |
386 |
|
387 |
static void get_transmittance(DATARRAY *tau_dp, const double radius, const double mu, const double d, |
388 |
const int intersects_ground, double *result) { |
389 |
|
390 |
DATARRAY *result1; |
391 |
DATARRAY *result2; |
392 |
|
393 |
const double r_d = clamp_radius(sqrt(d * d + 2.0 * radius * mu * d + radius * radius)); |
394 |
const double mu_d = clamp_cosine((radius * mu + d) / r_d); |
395 |
|
396 |
if (intersects_ground) { |
397 |
result1 = get_transmittance_to_space(tau_dp, r_d, -mu_d); |
398 |
result2 = get_transmittance_to_space(tau_dp, radius, -mu); |
399 |
} else { |
400 |
result1 = get_transmittance_to_space(tau_dp, radius, mu); |
401 |
result2 = get_transmittance_to_space(tau_dp, r_d, mu_d); |
402 |
} |
403 |
for (int i = 0; i < NSSAMP; ++i) { |
404 |
result[i] = fmin(result1->arr.d[i] / result2->arr.d[i], 1.0); |
405 |
} |
406 |
free(result1); |
407 |
free(result2); |
408 |
} |
409 |
|
410 |
void get_transmittance_to_sun(DATARRAY *tau_dp, const double r, |
411 |
const double mu_s, double *result) { |
412 |
const double sin_theta_h = ER / r; |
413 |
const double cos_theta_h = -sqrt(fmax(1.0 - sin_theta_h * sin_theta_h, 0.0)); |
414 |
DATARRAY *tau_sun = get_transmittance_to_space(tau_dp, r, mu_s); |
415 |
/* Smooth step */ |
416 |
const double edge0 = -sin_theta_h * SUNRAD; |
417 |
const double edge1 = edge0 * -1; |
418 |
double x = mu_s - cos_theta_h; |
419 |
x = (x - edge0) / (edge1 - edge0); |
420 |
x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x; |
421 |
const double st = x * x * (3.0 - 2.0 * x); |
422 |
for (int i = 0; i < NSSAMP; ++i) { |
423 |
result[i] = tau_sun->arr.d[i] * st; |
424 |
} |
425 |
free(tau_sun); |
426 |
} |
427 |
|
428 |
static void compute_single_scattering_integrand( |
429 |
const Atmosphere *atmos, DATARRAY *tau_dp, const double radius, const double mu, |
430 |
const double mu_s, const double nu, const double distance, |
431 |
const int r_mu_intersects_ground, double *rayleigh, double *mie) { |
432 |
|
433 |
const double radius_d = clamp_radius(sqrt(distance * distance + 2.0 * radius * mu * distance + radius * radius)); |
434 |
const double mu_s_d = clamp_cosine((radius * mu_s + distance * nu) / radius_d); |
435 |
/* double transmittance[NSSAMP]; */ |
436 |
double tau_r_mu[NSSAMP] = {0}; |
437 |
double tau_sun[NSSAMP] = {0}; |
438 |
get_transmittance(tau_dp, radius, mu, distance, r_mu_intersects_ground, tau_r_mu); |
439 |
get_transmittance_to_sun(tau_dp, radius_d, mu_s_d, tau_sun); |
440 |
const double rayleigh_profile_density = |
441 |
get_profile_density(&atmos->rayleigh_density, radius_d - ER); |
442 |
DATARRAY *mie_scat; |
443 |
double pt[1] = {radius_d - ER}; |
444 |
mie_scat = datavector(atmos->beta_m, pt); |
445 |
for (int i = 0; i < NSSAMP; ++i) { |
446 |
double transmittance = tau_r_mu[i] * tau_sun[i]; |
447 |
rayleigh[i] = transmittance * rayleigh_profile_density; |
448 |
mie[i] = transmittance * mie_scat->arr.d[i]; |
449 |
} |
450 |
free(mie_scat); |
451 |
} |
452 |
|
453 |
static double |
454 |
distance_to_nearst_atmosphere_boundary(const double radius, const double mu, |
455 |
const int hits_ground) { |
456 |
if (hits_ground) { |
457 |
return distance_to_earth(radius, mu); |
458 |
} else { |
459 |
return distance_to_space(radius, mu); |
460 |
} |
461 |
} |
462 |
|
463 |
static void compute_single_scattering(const Atmosphere *atmos, DATARRAY *tau_dp, |
464 |
const double radius, const double mu, |
465 |
const double mu_s, const double nu, |
466 |
const int hits_ground, |
467 |
float *rayleigh, float *mie) { |
468 |
|
469 |
const int nsamp = 50; |
470 |
double distance_to_boundary; |
471 |
|
472 |
if (hits_ground) { |
473 |
distance_to_boundary = distance_to_earth(radius, mu); |
474 |
} else { |
475 |
distance_to_boundary = distance_to_space(radius, mu); |
476 |
} |
477 |
const double dx = distance_to_boundary / (double)nsamp; |
478 |
|
479 |
double rayleigh_sum[NSSAMP] = {0}; |
480 |
double mie_sum[NSSAMP] = {0}; |
481 |
|
482 |
for (int i = 0; i <= nsamp; ++i) { |
483 |
const double d_i = i * dx; |
484 |
double rayleigh_i[NSSAMP] = {0}; |
485 |
double mie_i[NSSAMP] = {0}; |
486 |
compute_single_scattering_integrand(atmos, tau_dp, radius, mu, mu_s, nu, d_i, |
487 |
hits_ground, rayleigh_i, |
488 |
mie_i); |
489 |
double weight_i = (i == 0 || i == nsamp) ? 0.5 : 1.0; |
490 |
for (int j = 0; j < NSSAMP; ++j) { |
491 |
rayleigh_sum[j] += rayleigh_i[j] * weight_i; |
492 |
mie_sum[j] += mie_i[j] * weight_i; |
493 |
} |
494 |
} |
495 |
for (int i = 0; i < NSSAMP; ++i) { |
496 |
rayleigh[i] = rayleigh_sum[i] * dx * EXTSOL[i] * atmos->beta_r0[i]; |
497 |
mie[i] = dx * EXTSOL[i] * (mie_sum[i] * atmos->beta_scale); |
498 |
} |
499 |
} |
500 |
|
501 |
inline static double rayleigh_phase_function(const double nu) { |
502 |
const double k = 3.0 / (16.0 * PI); |
503 |
return k * (1.0 + nu * nu); |
504 |
} |
505 |
|
506 |
inline static double mie_phase_function(const double g, const double nu) { |
507 |
const double k = 3.0 / (8.0 * PI) * (1.0 - g * g) / (2.0 + g * g); |
508 |
return k * (1.0 + nu * nu) / pow(1.0 + g * g - 2.0 * g * nu, 1.5); |
509 |
} |
510 |
|
511 |
inline static double cloud_phase_function(const double g, const double nu) { |
512 |
const double alpha = 0.5; |
513 |
const double g1 = -0.5; |
514 |
const double hg0 = mie_phase_function(g, nu); |
515 |
const double hg1 = mie_phase_function(g1, nu); |
516 |
return alpha * hg0 + (1.0 - alpha) * hg1; |
517 |
} |
518 |
|
519 |
static void to_scattering_uvwz(const double radius, const double mu, const double mu_s, const double nu, |
520 |
const int r_mu_hits_ground, double *u, double *v, |
521 |
double *w, double *z) { |
522 |
|
523 |
double H = sqrt(AR * AR - ER * ER); |
524 |
double rho = safe_sqrt(radius * radius - ER * ER); |
525 |
double u_r = rho / H; |
526 |
|
527 |
double r_mu = radius * mu; |
528 |
double discriminant = r_mu * r_mu - radius * radius + ER * ER; |
529 |
|
530 |
double u_mu; |
531 |
if (r_mu_hits_ground) { |
532 |
double d = -r_mu - safe_sqrt(discriminant); |
533 |
double d_min = radius - ER; |
534 |
double d_max = rho; |
535 |
u_mu = 0.5 - 0.5 * get_texture_coord_from_unit_range( |
536 |
d_max == d_min ? 0.0 : (d - d_min) / (d_max - d_min), |
537 |
SCATTERING_TEXTURE_MU_SIZE / 2); |
538 |
} else { |
539 |
double d = -r_mu + sqrt(discriminant + H * H); |
540 |
double d_min = AR - radius; |
541 |
double d_max = rho + H; |
542 |
u_mu = 0.5 + 0.5 * get_texture_coord_from_unit_range( |
543 |
(d - d_min) / (d_max - d_min), |
544 |
SCATTERING_TEXTURE_MU_SIZE / 2); |
545 |
} |
546 |
|
547 |
double d = distance_to_space(ER, mu_s); |
548 |
double d_min = AH; |
549 |
double d_max = H; |
550 |
double a = (d - d_min) / (d_max - d_min); |
551 |
double D = distance_to_space(ER, MU_S_MIN); |
552 |
double A = (D - d_min) / (d_max - d_min); |
553 |
double u_mu_s = get_texture_coord_from_unit_range( |
554 |
fmax(1.0 - a / A, 0.0) / (1.0 + a), SCATTERING_TEXTURE_MU_S_SIZE); |
555 |
double u_nu = (nu + 1.0) / 2; |
556 |
*u = u_nu; |
557 |
*v = u_mu_s; |
558 |
*w = u_mu; |
559 |
*z = u_r; |
560 |
} |
561 |
|
562 |
static void from_scattering_uvwz(const double x, const double y, const double z, const double w, |
563 |
double *radius, double *mu, double *mu_s, |
564 |
double *nu, int *r_mu_hits_ground) { |
565 |
|
566 |
|
567 |
const double H = sqrt(AR * AR - ER * ER); |
568 |
const double rho = H * w; |
569 |
*radius = sqrt(rho * rho + ER * ER); |
570 |
|
571 |
if (z < 0.5) { |
572 |
/* Distance to the ground for the ray (r,mu), and its minimum and maximum */ |
573 |
/* values over all mu - obtained for (r,-1) and (r,mu_horizon) - from which */ |
574 |
/* we can recover mu: */ |
575 |
const double d_min = *radius - ER; |
576 |
const double d_max = rho; |
577 |
const double d = d_min + (d_max - d_min) * |
578 |
get_unit_range_from_texture_coord( |
579 |
1.0 - 2.0 * z, SCATTERING_TEXTURE_MU_SIZE / 2); |
580 |
*mu = d == 0.0 ? -1.0 : clamp_cosine(-(rho * rho + d * d) / (2.0 * *radius * d)); |
581 |
*r_mu_hits_ground = 1; |
582 |
} else { |
583 |
/* Distance to the top atmosphere boundary for the ray (r,mu), and its */ |
584 |
/* minimum and maximum values over all mu - obtained for (r,1) and */ |
585 |
/* (r,mu_horizon) - from which we can recover mu: */ |
586 |
const double d_min = AR - *radius; |
587 |
const double d_max = rho + H; |
588 |
const double d = d_min + (d_max - d_min) * |
589 |
get_unit_range_from_texture_coord( |
590 |
2.0 * z - 1.0, SCATTERING_TEXTURE_MU_SIZE / 2); |
591 |
*mu = d == 0.0 |
592 |
? 1.0 |
593 |
: clamp_cosine((H * H - rho * rho - d * d) / (2.0 * (*radius) * d)); |
594 |
*r_mu_hits_ground = 0; |
595 |
} |
596 |
|
597 |
const double x_mu_s = y; |
598 |
const double d_min = AH; |
599 |
const double d_max = H; |
600 |
const double D = distance_to_space(ER, MU_S_MIN); |
601 |
const double A = (D - d_min) / (d_max - d_min); |
602 |
const double a = (A - x_mu_s * A) / (1.0 + x_mu_s * A); |
603 |
const double d = d_min + fmin(a, A) * (d_max - d_min); |
604 |
*mu_s = d == 0.0 ? 1.0 : clamp_cosine((H * H - d * d) / (2.0 * ER * d)); |
605 |
*nu = clamp_cosine(x * 2.0 - 1.0); |
606 |
} |
607 |
|
608 |
static DATARRAY *interpolate_scattering(DATARRAY *dp, const double radius, |
609 |
const double mu, const double mu_s, |
610 |
const double nu, |
611 |
const int r_mu_hits_ground) { |
612 |
double u, v, w, z; |
613 |
to_scattering_uvwz(radius, mu, mu_s, nu, r_mu_hits_ground, &u, &v, &w, |
614 |
&z); |
615 |
double pt[4] = {z, w, v, u}; |
616 |
return datavector(dp, pt); |
617 |
} |
618 |
|
619 |
static void get_scattering(DATARRAY *scat1r, DATARRAY *scat1m, DATARRAY *scat, |
620 |
const double radius, const double mu, const double mu_s, |
621 |
const double nu, |
622 |
const int ray_r_mu_intersects_ground, |
623 |
const int scattering_order, double *result) { |
624 |
if (scattering_order == 1) { |
625 |
DATARRAY *rayleigh = interpolate_scattering(scat1r, radius, mu, mu_s, nu, |
626 |
ray_r_mu_intersects_ground); |
627 |
DATARRAY *mie = interpolate_scattering(scat1m, radius, mu, mu_s, nu, |
628 |
ray_r_mu_intersects_ground); |
629 |
const double rayleigh_phase = rayleigh_phase_function(nu); |
630 |
const double mie_phase = mie_phase_function(MIE_G, nu); |
631 |
for (int i = 0; i < NSSAMP; ++i) { |
632 |
result[i] = |
633 |
rayleigh->arr.d[i] * rayleigh_phase + mie->arr.d[i] * mie_phase; |
634 |
} |
635 |
free(rayleigh); |
636 |
free(mie); |
637 |
} else { |
638 |
DATARRAY *scattering = interpolate_scattering(scat, radius, mu, mu_s, nu, |
639 |
ray_r_mu_intersects_ground); |
640 |
for (int i = 0; i < NSSAMP; ++i) { |
641 |
result[i] = scattering->arr.d[i]; |
642 |
} |
643 |
free(scattering); |
644 |
} |
645 |
} |
646 |
|
647 |
static void to_irradiance_uv(const double radius, const double mu_s, double *u, |
648 |
double *v) { |
649 |
const double x_r = (radius - ER) / AH; |
650 |
const double x_mu_s = mu_s * 0.5 + 0.5; |
651 |
*u = x_mu_s; |
652 |
*v = x_r; |
653 |
} |
654 |
|
655 |
static void from_irradiance_uv(const double u, const double v, double *radius, |
656 |
double *mu_s) { |
657 |
*radius = v * AH + ER; |
658 |
*mu_s = clamp_cosine(u * 2.0 - 1.0); |
659 |
} |
660 |
|
661 |
DATARRAY *get_indirect_irradiance(DATARRAY *dp, const double radius, |
662 |
const double mu_s) { |
663 |
double u, v; |
664 |
to_irradiance_uv(radius, mu_s, &u, &v); |
665 |
double pt[2] = {v, u}; |
666 |
return datavector(dp, pt); |
667 |
} |
668 |
|
669 |
static void |
670 |
compute_scattering_density(const Atmosphere *atmos, DATARRAY *tau_dp, |
671 |
DATARRAY *scat1r, DATARRAY *scat1m, DATARRAY *scat, |
672 |
DATARRAY *irrad_dp, const double radius, const double mu, |
673 |
const double mu_s, const double nu, |
674 |
const int scattering_order, double *result) { |
675 |
/* Compute unit direction vectors for the zenith, the view direction omega and */ |
676 |
/* and the sun direction omega_s, such that the cosine of the view-zenith */ |
677 |
/* angle is mu, the cosine of the sun-zenith angle is mu_s, and the cosine of */ |
678 |
/* the view-sun angle is nu. The goal is to simplify computations below. */ |
679 |
const double height = radius - ER; |
680 |
const double epsilon = 1e-6; |
681 |
const FVECT zenith_direction = {0.0, 0.0, 1.0}; |
682 |
double omega_0 = 1.0 - mu * mu; |
683 |
omega_0 = fabs(omega_0) <= epsilon ? 0.0 : sqrt(omega_0); |
684 |
const FVECT omega = {omega_0, 0.0, mu}; |
685 |
const double sun_dir_x = omega[0] == 0.0 ? 0.0 : (nu - mu * mu_s) / omega[0]; |
686 |
const double sun_dir_y = |
687 |
sqrt(fmax(1.0 - sun_dir_x * sun_dir_x - mu_s * mu_s, 0.0)); |
688 |
const FVECT omega_s = {sun_dir_x, sun_dir_y, mu_s}; |
689 |
|
690 |
const int SAMPLE_COUNT = 16; |
691 |
const double dphi = PI / SAMPLE_COUNT; |
692 |
const double dtheta = PI / SAMPLE_COUNT; |
693 |
|
694 |
/* Nested loops for the integral over all the incident directions omega_i. */ |
695 |
for (int l = 0; l < SAMPLE_COUNT; ++l) { |
696 |
const double theta = (l + 0.5) * dtheta; |
697 |
const double cos_theta = cos(theta); |
698 |
const double sin_theta = sin(theta); |
699 |
const int r_theta_hits_ground = ray_hits_ground(radius, cos_theta); |
700 |
|
701 |
/* The distance and transmittance to the ground only depend on theta, so we */ |
702 |
/* can compute them in the outer loop for efficiency. */ |
703 |
double distance_to_ground = 0.0; |
704 |
double transmittance_to_ground[NSSAMP] = {0}; |
705 |
double ground_albedo = 0; |
706 |
if (r_theta_hits_ground) { |
707 |
distance_to_ground = distance_to_earth(radius, cos_theta); |
708 |
/* assert(distance_to_ground >= 0.0 && distance_to_ground <= HMAX); */ |
709 |
get_transmittance(tau_dp, radius, cos_theta, distance_to_ground, r_theta_hits_ground, |
710 |
transmittance_to_ground); |
711 |
ground_albedo = atmos->grefl; |
712 |
} |
713 |
|
714 |
for (int m = 0; m < 2 * SAMPLE_COUNT; ++m) { |
715 |
const double phi = (m + 0.5) * dphi; |
716 |
const FVECT omega_i = {cos(phi) * sin_theta, sin(phi) * sin_theta, |
717 |
cos_theta}; |
718 |
const double domega_i = dtheta * dphi * sin(theta); |
719 |
|
720 |
/* The radiance L_i arriving from direction omega_i after n-1 bounces is */ |
721 |
/* the sum of a term given by the precomputed scattering texture for the */ |
722 |
/* (n-1)-th order: */ |
723 |
double nu1 = fdot(omega_s, omega_i); |
724 |
if (nu1 <= -1.0) { |
725 |
nu1 += 0.1; |
726 |
} else if (nu1 >= 1.0) { |
727 |
nu1 -= 0.1; |
728 |
} |
729 |
double incident_radiance[NSSAMP] = {0}; |
730 |
get_scattering(scat1r, scat1m, scat, radius, omega_i[2], mu_s, nu1, |
731 |
r_theta_hits_ground, scattering_order - 1, |
732 |
incident_radiance); |
733 |
|
734 |
/* and of the contribution from the light paths with n-1 bounces and whose */ |
735 |
/* last bounce is on the ground. This contribution is the product of the */ |
736 |
/* transmittance to the ground, the ground albedo, the ground BRDF, and */ |
737 |
/* the irradiance received on the ground after n-2 bounces. */ |
738 |
FVECT ground_normal = { |
739 |
zenith_direction[0] * radius + omega_i[0] * distance_to_ground, |
740 |
zenith_direction[1] * radius + omega_i[1] * distance_to_ground, |
741 |
zenith_direction[2] * radius + omega_i[2] * distance_to_ground}; |
742 |
normalize(ground_normal); |
743 |
DATARRAY *ground_irradiance = |
744 |
get_indirect_irradiance(irrad_dp, ER, fdot(ground_normal, omega_s)); |
745 |
for (int i = 0; i < NSSAMP; ++i) { |
746 |
incident_radiance[i] += transmittance_to_ground[i] * ground_albedo * |
747 |
(1.0 / PI) * ground_irradiance->arr.d[i]; |
748 |
} |
749 |
free(ground_irradiance); |
750 |
|
751 |
/* The radiance finally scattered from direction omega_i towards direction */ |
752 |
/* -omega is the product of the incident radiance, the scattering */ |
753 |
/* coefficient, and the phase function for directions omega and omega_i */ |
754 |
/* (all this summed over all particle types, i.e. Rayleigh and Mie). */ |
755 |
const double nu2 = fdot(omega, omega_i); |
756 |
const double rayleigh_density = |
757 |
get_profile_density(&atmos->rayleigh_density, height); |
758 |
DATARRAY *mie_scat; |
759 |
double pt[1] = {height}; |
760 |
mie_scat = datavector(atmos->beta_m, pt); |
761 |
const double rayleigh_phase = rayleigh_phase_function(nu2); |
762 |
const double mie_phase = mie_phase_function(MIE_G, nu2); |
763 |
for (int j = 0; j < NSSAMP; ++j) { |
764 |
result[j] += incident_radiance[j] * domega_i * |
765 |
(atmos->beta_r0[j] * rayleigh_density * rayleigh_phase + |
766 |
mie_scat->arr.d[j] * mie_phase * atmos->beta_scale); |
767 |
} |
768 |
free(mie_scat); |
769 |
} |
770 |
} |
771 |
} |
772 |
|
773 |
static void compute_multi_scattering(DATARRAY *tau_dp, |
774 |
DATARRAY *scattering_density_dp, |
775 |
const double radius, const double mu, |
776 |
const double mu_s, const double nu, |
777 |
const int r_mu_hits_ground, |
778 |
double *result) { |
779 |
|
780 |
|
781 |
/* Numerical integration sample count. */ |
782 |
const int nsamp = 50; |
783 |
const double dx = distance_to_nearst_atmosphere_boundary( |
784 |
radius, mu, r_mu_hits_ground) / |
785 |
(double)nsamp; |
786 |
|
787 |
for (int i = 0; i <= nsamp; ++i) { |
788 |
const double d_i = i * dx; |
789 |
|
790 |
/* The r, mu and mu_s parameters at the current integration point (see the */ |
791 |
/* single scattering section for a detailed explanation). */ |
792 |
const double r_i = |
793 |
clamp_radius(sqrt(d_i * d_i + 2.0 * radius * mu * d_i + radius * radius)); |
794 |
const double mu_i = clamp_cosine((radius * mu + d_i) / r_i); |
795 |
const double mu_s_i = clamp_cosine((radius * mu_s + d_i * nu) / r_i); |
796 |
|
797 |
/* The Rayleigh and Mie multiple scattering at the current sample point. */ |
798 |
double transmittance[NSSAMP] = {0}; |
799 |
DATARRAY *rayleigh_mie_s = |
800 |
interpolate_scattering(scattering_density_dp, r_i, mu_i, mu_s_i, nu, |
801 |
r_mu_hits_ground); |
802 |
get_transmittance(tau_dp, radius, mu, d_i, r_mu_hits_ground, |
803 |
transmittance); |
804 |
/* Sample weight (from the trapezoidal rule). */ |
805 |
const double weight_i = (i == 0 || i == nsamp) ? 0.5 : 1.0; |
806 |
for (int j = 0; j < NSSAMP; ++j) { |
807 |
result[j] += rayleigh_mie_s->arr.d[j] * transmittance[j] * dx * weight_i; |
808 |
} |
809 |
free(rayleigh_mie_s); |
810 |
} |
811 |
} |
812 |
|
813 |
static void compute_direct_irradiance(DATARRAY *tau_dp, const double r, |
814 |
const double mu_s, float *result) { |
815 |
|
816 |
/* Approximate the average of the cosine factor mu_s over the visible fraction */ |
817 |
/* of the sun disc */ |
818 |
const double average_cosine_factor = |
819 |
mu_s < -SUNRAD |
820 |
? 0.0 |
821 |
: (mu_s > SUNRAD |
822 |
? mu_s |
823 |
: (mu_s + SUNRAD) * (mu_s + SUNRAD) / (4.0 * SUNRAD)); |
824 |
|
825 |
DATARRAY *transmittance = get_transmittance_to_space(tau_dp, r, mu_s); |
826 |
for (int i = 0; i < NSSAMP; ++i) { |
827 |
result[i] = EXTSOL[i] * transmittance->arr.d[i] * average_cosine_factor; |
828 |
} |
829 |
free(transmittance); |
830 |
} |
831 |
|
832 |
static void compute_indirect_irradiance(DATARRAY *scat1r, DATARRAY *scat1m, |
833 |
DATARRAY *scat, const double radius, |
834 |
const double mu_s, |
835 |
const int scattering_order, |
836 |
double *result) { |
837 |
|
838 |
const int SAMPLE_COUNT = 32; |
839 |
const double dphi = PI / SAMPLE_COUNT; |
840 |
const double dtheta = PI / SAMPLE_COUNT; |
841 |
|
842 |
const FVECT omega_s = {sqrt(1.0 - mu_s * mu_s), 0.0, mu_s}; |
843 |
for (int j = 0; j < SAMPLE_COUNT / 2; ++j) { |
844 |
const double theta = (j + 0.5) * dtheta; |
845 |
for (int i = 0; i < 2 * SAMPLE_COUNT; ++i) { |
846 |
const double phi = (i + 0.5) * dphi; |
847 |
const FVECT omega = {sin(theta) * cos(phi), sin(theta) * sin(phi), |
848 |
cos(theta)}; |
849 |
const double domega = dtheta * dphi * sin(theta); |
850 |
const double nu = fdot(omega, omega_s); |
851 |
double result1[NSSAMP] = {0}; |
852 |
get_scattering(scat1r, scat1m, scat, radius, omega[2], mu_s, nu, 0, |
853 |
scattering_order, result1); |
854 |
for (int k = 0; k < NSSAMP; ++k) { |
855 |
result[k] += result1[k] * omega[2] * domega; |
856 |
} |
857 |
} |
858 |
} |
859 |
} |
860 |
|
861 |
DATARRAY *allocate_3d_datarray(const char *name, const int ri, const int rj, |
862 |
const int rk) { |
863 |
DATARRAY *dp; |
864 |
if ((dp = (DATARRAY *)malloc(sizeof(DATARRAY))) == NULL) |
865 |
goto memerr; |
866 |
int asize = ri * rj * rk; |
867 |
dp->name = savestr(name); |
868 |
dp->type = DATATY; |
869 |
dp->nd = 3; |
870 |
dp->dim[0].org = 0; |
871 |
dp->dim[0].siz = 1; |
872 |
dp->dim[0].ne = ri; |
873 |
dp->dim[0].p = NULL; |
874 |
dp->dim[1].org = 0; |
875 |
dp->dim[1].siz = 1; |
876 |
dp->dim[1].ne = rj; |
877 |
dp->dim[1].p = NULL; |
878 |
dp->dim[2].org = 0; |
879 |
dp->dim[2].siz = rk - 1; |
880 |
dp->dim[2].ne = rk; |
881 |
dp->dim[2].p = NULL; |
882 |
/* dp->arr.p = */ |
883 |
/* malloc(sizeof(DATATYPE)*dp->dim[0].ne*dp->dim[1].ne*dp->dim[2].ne); */ |
884 |
if ((dp->arr.d = (DATATYPE *)malloc(asize * sizeof(DATATYPE))) == NULL) |
885 |
goto memerr; |
886 |
return (dp); |
887 |
|
888 |
memerr: |
889 |
fprintf(stderr, "Memory allocation error in allocate_3d_datarray\n"); |
890 |
return (NULL); |
891 |
} |
892 |
|
893 |
DATARRAY *allocate_5d_datarray(const char *name, const int ri, const int rj, |
894 |
const int rk, const int rl, const int rm) { |
895 |
DATARRAY *dp; |
896 |
int asize = ri * rj * rk * rl * rm; |
897 |
if ((dp = (DATARRAY *)malloc(sizeof(DATARRAY))) == NULL) |
898 |
goto memerr; |
899 |
dp->name = savestr(name); |
900 |
dp->type = DATATY; |
901 |
dp->nd = 5; |
902 |
dp->dim[0].org = 0; |
903 |
dp->dim[0].siz = 1; |
904 |
dp->dim[0].ne = ri; |
905 |
dp->dim[0].p = NULL; |
906 |
dp->dim[1].org = 0; |
907 |
dp->dim[1].siz = 1; |
908 |
dp->dim[1].ne = rj; |
909 |
dp->dim[1].p = NULL; |
910 |
dp->dim[2].org = 0; |
911 |
dp->dim[2].siz = 1; |
912 |
dp->dim[2].ne = rk; |
913 |
dp->dim[2].p = NULL; |
914 |
dp->dim[3].org = 0; |
915 |
dp->dim[3].siz = 1; |
916 |
dp->dim[3].ne = rl; |
917 |
dp->dim[3].p = NULL; |
918 |
dp->dim[4].org = 0; |
919 |
dp->dim[4].siz = rm - 1; |
920 |
dp->dim[4].ne = rm; |
921 |
dp->dim[4].p = NULL; |
922 |
if ((dp->arr.d = (DATATYPE *)malloc(asize * sizeof(DATATYPE))) == NULL) |
923 |
goto memerr; |
924 |
return (dp); |
925 |
memerr: |
926 |
fprintf(stderr, "Memory allocation error in allocate_5d_datarray\n"); |
927 |
return (NULL); |
928 |
} |
929 |
|
930 |
/* save to a .dat file */ |
931 |
void savedata(DATARRAY *dp) { |
932 |
if (dp == NULL) |
933 |
return; |
934 |
FILE *fp = fopen(dp->name, "w"); |
935 |
if (fp == NULL) { |
936 |
fprintf(stderr, "Error opening file %s\n", dp->name); |
937 |
return; |
938 |
} |
939 |
int i, j; |
940 |
int nvals = 1; |
941 |
for (i = 0; i < dp->nd; i++) { |
942 |
nvals *= dp->dim[i].ne; |
943 |
} |
944 |
fprintf(fp, "%d\n", dp->nd); |
945 |
for (i = 0; i < dp->nd; i++) { |
946 |
fprintf(fp, "%f %f %d\n", dp->dim[i].org, dp->dim[i].siz, dp->dim[i].ne); |
947 |
} |
948 |
for (i = 0; i < nvals; i++) { |
949 |
fprintf(fp, "%f\n", dp->arr.d[i]); |
950 |
} |
951 |
fclose(fp); |
952 |
} |
953 |
|
954 |
void increment_dp(DATARRAY *dp1, DATARRAY *dp2) { |
955 |
if (dp1 == NULL || dp2 == NULL) |
956 |
perror("null pointer in increment_dp\n"); |
957 |
|
958 |
if (dp1->nd != dp2->nd) |
959 |
perror("dimension mismatch in increment_dp\n"); |
960 |
|
961 |
int i; |
962 |
int nvals1 = 1; |
963 |
int nvals2 = 1; |
964 |
for (i = 0; i < dp1->nd; i++) { |
965 |
nvals1 *= dp1->dim[i].ne; |
966 |
nvals2 *= dp2->dim[i].ne; |
967 |
} |
968 |
if (nvals1 != nvals2) |
969 |
perror("size mismatch in increment_dp\n"); |
970 |
for (i = 0; i < nvals1; i++) { |
971 |
dp1->arr.d[i] += dp2->arr.d[i]; |
972 |
} |
973 |
} |
974 |
|
975 |
THREAD_RETURN compute_scattering_density_thread(void *arg) { |
976 |
ScatDenseTdat *tdata = (ScatDenseTdat *)arg; |
977 |
for (unsigned int l = tdata->start_l; l < tdata->end_l; ++l) { |
978 |
for (unsigned int k = 0; k < SCATTERING_TEXTURE_MU_SIZE; ++k) { |
979 |
for (unsigned int j = 0; j < SCATTERING_TEXTURE_MU_S_SIZE; ++j) { |
980 |
for (unsigned int i = 0; i < SCATTERING_TEXTURE_NU_SIZE; ++i) { |
981 |
double scattering_density[NSSAMP] = {0}; |
982 |
double r, mu, mu_s, nu; |
983 |
int ray_r_mu_intersects_ground; |
984 |
double xr = (double)i / SCATTERING_TEXTURE_NU_SIZE; |
985 |
double yr = (double)j / SCATTERING_TEXTURE_MU_S_SIZE; |
986 |
double zr = (double)k / SCATTERING_TEXTURE_MU_SIZE; |
987 |
double wr = (double)l / SCATTERING_TEXTURE_R_SIZE; |
988 |
from_scattering_uvwz(xr, yr, zr, wr, &r, &mu, &mu_s, &nu, |
989 |
&ray_r_mu_intersects_ground); |
990 |
nu = |
991 |
fmax(mu * mu_s - sqrt((1.0 - mu * mu) * (1.0 - mu_s * mu_s)), |
992 |
fmin(mu * mu_s + sqrt((1.0 - mu * mu) * (1.0 - mu_s * mu_s)), |
993 |
nu)); |
994 |
compute_scattering_density( |
995 |
tdata->atmos, tdata->tau_dp, tdata->delta_rayleigh_scattering_dp, |
996 |
tdata->delta_mie_scattering_dp, |
997 |
tdata->delta_multiple_scattering_dp, tdata->delta_irradiance_dp, |
998 |
r, mu, mu_s, nu, tdata->scattering_order, scattering_density); |
999 |
for (int m = 0; m < NSSAMP; ++m) { |
1000 |
int idx = l * SCATTERING_TEXTURE_MU_SIZE * |
1001 |
SCATTERING_TEXTURE_MU_S_SIZE * |
1002 |
SCATTERING_TEXTURE_NU_SIZE * NSSAMP + |
1003 |
k * SCATTERING_TEXTURE_MU_S_SIZE * |
1004 |
SCATTERING_TEXTURE_NU_SIZE * NSSAMP + |
1005 |
j * SCATTERING_TEXTURE_NU_SIZE * NSSAMP + i * NSSAMP + m; |
1006 |
tdata->delta_scattering_density_dp->arr.d[idx] = |
1007 |
scattering_density[m]; |
1008 |
} |
1009 |
} |
1010 |
} |
1011 |
} |
1012 |
} |
1013 |
return 0; |
1014 |
} |
1015 |
|
1016 |
THREAD_RETURN compute_single_scattering_thread(void *arg) { |
1017 |
Scat1Tdat *tdata = (Scat1Tdat *)arg; |
1018 |
for (unsigned int l = tdata->start_l; l < tdata->end_l; ++l) { |
1019 |
for (int k = 0; k < SCATTERING_TEXTURE_MU_SIZE; ++k) { |
1020 |
for (int j = 0; j < SCATTERING_TEXTURE_MU_S_SIZE; ++j) { |
1021 |
for (int i = 0; i < SCATTERING_TEXTURE_NU_SIZE; ++i) { |
1022 |
float rayleigh[NSSAMP] = {0}; |
1023 |
float mie[NSSAMP] = {0}; |
1024 |
double r, mu, mu_s, nu; |
1025 |
int r_mu_hits_ground; |
1026 |
const double xr = (double)i / SCATTERING_TEXTURE_NU_SIZE; |
1027 |
const double yr = (double)j / SCATTERING_TEXTURE_MU_S_SIZE; |
1028 |
const double zr = (double)k / SCATTERING_TEXTURE_MU_SIZE; |
1029 |
const double wr = (double)l / SCATTERING_TEXTURE_R_SIZE; |
1030 |
from_scattering_uvwz(xr, yr, zr, wr, &r, &mu, &mu_s, &nu, |
1031 |
&r_mu_hits_ground); |
1032 |
const double mu_mu_s = mu * mu_s; |
1033 |
const double sqrt_term = sqrt((1.0 - mu * mu) * (1.0 - mu_s * mu_s)); |
1034 |
nu = fmax(mu_mu_s - sqrt_term, fmin(mu_mu_s + sqrt_term, nu)); |
1035 |
compute_single_scattering(tdata->atmos, tdata->tau_dp, r, mu, mu_s, |
1036 |
nu, r_mu_hits_ground, rayleigh, |
1037 |
mie); |
1038 |
for (int m = 0; m < NSSAMP; ++m) { |
1039 |
int idx = l * SCATTERING_TEXTURE_MU_SIZE * |
1040 |
SCATTERING_TEXTURE_MU_S_SIZE * |
1041 |
SCATTERING_TEXTURE_NU_SIZE * NSSAMP + |
1042 |
k * SCATTERING_TEXTURE_MU_S_SIZE * |
1043 |
SCATTERING_TEXTURE_NU_SIZE * NSSAMP + |
1044 |
j * SCATTERING_TEXTURE_NU_SIZE * NSSAMP + i * NSSAMP + m; |
1045 |
tdata->delta_rayleigh_scattering_dp->arr.d[idx] = rayleigh[m]; |
1046 |
tdata->delta_mie_scattering_dp->arr.d[idx] = mie[m]; |
1047 |
tdata->scattering_dp->arr.d[idx] = rayleigh[m]; |
1048 |
} |
1049 |
} |
1050 |
} |
1051 |
} |
1052 |
} |
1053 |
return 0; |
1054 |
} |
1055 |
|
1056 |
THREAD_RETURN compute_multi_scattering_thread(void *arg) { |
1057 |
ScatNTdat *tdata = (ScatNTdat *)arg; |
1058 |
for (unsigned int l = tdata->start_l; l < tdata->end_l; ++l) { |
1059 |
for (unsigned int k = 0; k < SCATTERING_TEXTURE_MU_SIZE; ++k) { |
1060 |
for (unsigned int j = 0; j < SCATTERING_TEXTURE_MU_S_SIZE; ++j) { |
1061 |
for (unsigned int i = 0; i < SCATTERING_TEXTURE_NU_SIZE; ++i) { |
1062 |
double delta_multiple_scattering[NSSAMP] = {0}; |
1063 |
double r, mu, mu_s, nu; |
1064 |
int r_mu_hits_ground; |
1065 |
const double xr = (double)i / SCATTERING_TEXTURE_NU_SIZE; |
1066 |
const double yr = (double)j / SCATTERING_TEXTURE_MU_S_SIZE; |
1067 |
const double zr = (double)k / SCATTERING_TEXTURE_MU_SIZE; |
1068 |
const double wr = (double)l / SCATTERING_TEXTURE_R_SIZE; |
1069 |
from_scattering_uvwz(xr, yr, zr, wr, &r, &mu, &mu_s, &nu, |
1070 |
&r_mu_hits_ground); |
1071 |
const double mu_mu_s = mu * mu_s; |
1072 |
const double sqrt_term = sqrt((1.0 - mu * mu) * (1.0 - mu_s * mu_s)); |
1073 |
nu = fmax(mu_mu_s - sqrt_term, fmin(mu_mu_s + sqrt_term, nu)); |
1074 |
compute_multi_scattering( |
1075 |
tdata->tau_dp, tdata->delta_scattering_density_dp, r, mu, mu_s, |
1076 |
nu, r_mu_hits_ground, delta_multiple_scattering); |
1077 |
const double rayleigh_phase = rayleigh_phase_function(nu); |
1078 |
for (int m = 0; m < NSSAMP; ++m) { |
1079 |
int idx = l * SCATTERING_TEXTURE_MU_SIZE * |
1080 |
SCATTERING_TEXTURE_MU_S_SIZE * |
1081 |
SCATTERING_TEXTURE_NU_SIZE * NSSAMP + |
1082 |
k * SCATTERING_TEXTURE_MU_S_SIZE * |
1083 |
SCATTERING_TEXTURE_NU_SIZE * NSSAMP + |
1084 |
j * SCATTERING_TEXTURE_NU_SIZE * NSSAMP + i * NSSAMP + m; |
1085 |
tdata->delta_multiple_scattering_dp->arr.d[idx] = |
1086 |
delta_multiple_scattering[m]; |
1087 |
tdata->scattering_dp->arr.d[idx] += |
1088 |
delta_multiple_scattering[m] * (1.0 / rayleigh_phase); |
1089 |
} |
1090 |
} |
1091 |
} |
1092 |
} |
1093 |
} |
1094 |
return 0; |
1095 |
} |
1096 |
|
1097 |
int precompute(const int sorder, const DpPaths dppaths, const Atmosphere *atmos, |
1098 |
int num_threads) { |
1099 |
num_threads = (num_threads < SCATTERING_TEXTURE_R_SIZE) |
1100 |
? num_threads |
1101 |
: SCATTERING_TEXTURE_R_SIZE; |
1102 |
const int tchunk = SCATTERING_TEXTURE_R_SIZE / num_threads; |
1103 |
const int tremainder = SCATTERING_TEXTURE_R_SIZE % num_threads; |
1104 |
|
1105 |
if (sorder < 2) { |
1106 |
fprintf(stderr, "scattering order must be at least 2\n"); |
1107 |
return 0; |
1108 |
} |
1109 |
|
1110 |
DATARRAY *tau_dp = |
1111 |
allocate_3d_datarray(dppaths.tau, TRANSMITTANCE_TEXTURE_HEIGHT, |
1112 |
TRANSMITTANCE_TEXTURE_WIDTH, NSSAMP); |
1113 |
DATARRAY *delta_irradiance_dp = allocate_3d_datarray( |
1114 |
"ssky_delta_irradiance.dat", IRRADIANCE_TEXTURE_HEIGHT, |
1115 |
IRRADIANCE_TEXTURE_WIDTH, NSSAMP); |
1116 |
DATARRAY *irradiance_dp = |
1117 |
allocate_3d_datarray(dppaths.irrad, IRRADIANCE_TEXTURE_HEIGHT, |
1118 |
IRRADIANCE_TEXTURE_WIDTH, NSSAMP); |
1119 |
|
1120 |
DATARRAY *delta_rayleigh_scattering_dp = allocate_5d_datarray( |
1121 |
"ssky_delta_rayleigh_scattering_dp.dat", SCATTERING_TEXTURE_R_SIZE, |
1122 |
SCATTERING_TEXTURE_MU_SIZE, SCATTERING_TEXTURE_MU_S_SIZE, |
1123 |
SCATTERING_TEXTURE_NU_SIZE, NSSAMP); |
1124 |
DATARRAY *delta_mie_scattering_dp = allocate_5d_datarray( |
1125 |
dppaths.scat1m, SCATTERING_TEXTURE_R_SIZE, SCATTERING_TEXTURE_MU_SIZE, |
1126 |
SCATTERING_TEXTURE_MU_S_SIZE, SCATTERING_TEXTURE_NU_SIZE, NSSAMP); |
1127 |
DATARRAY *scattering_dp = allocate_5d_datarray( |
1128 |
dppaths.scat, SCATTERING_TEXTURE_R_SIZE, SCATTERING_TEXTURE_MU_SIZE, |
1129 |
SCATTERING_TEXTURE_MU_S_SIZE, SCATTERING_TEXTURE_NU_SIZE, NSSAMP); |
1130 |
DATARRAY *delta_multiple_scattering_dp = allocate_5d_datarray( |
1131 |
"ssky_delta_multiple_scattering.dat", SCATTERING_TEXTURE_R_SIZE, |
1132 |
SCATTERING_TEXTURE_MU_SIZE, SCATTERING_TEXTURE_MU_S_SIZE, |
1133 |
SCATTERING_TEXTURE_NU_SIZE, NSSAMP); |
1134 |
DATARRAY *delta_scattering_density_dp = allocate_5d_datarray( |
1135 |
"ssky_delta_scattering_density.dat", SCATTERING_TEXTURE_R_SIZE, |
1136 |
SCATTERING_TEXTURE_MU_SIZE, SCATTERING_TEXTURE_MU_S_SIZE, |
1137 |
SCATTERING_TEXTURE_NU_SIZE, NSSAMP); |
1138 |
|
1139 |
int idx = 0; |
1140 |
/* Computing transmittance... */ |
1141 |
for (int j = 0; j < TRANSMITTANCE_TEXTURE_HEIGHT; ++j) { |
1142 |
for (int i = 0; i < TRANSMITTANCE_TEXTURE_WIDTH; ++i) { |
1143 |
double r, mu; |
1144 |
float tau[NSSAMP] = {0}; |
1145 |
const float xr = (float)i / TRANSMITTANCE_TEXTURE_WIDTH; |
1146 |
const float yr = (float)j / TRANSMITTANCE_TEXTURE_HEIGHT; |
1147 |
from_transmittance_uv(xr, yr, &r, &mu); |
1148 |
compute_transmittance_to_space(atmos, r, mu, tau); |
1149 |
for (int k = 0; k < NSSAMP; ++k) { |
1150 |
tau_dp->arr.d[idx] = tau[k]; |
1151 |
idx += 1; |
1152 |
} |
1153 |
} |
1154 |
} |
1155 |
savedata(tau_dp); |
1156 |
|
1157 |
/* Computing direct irradiance... */ |
1158 |
idx = 0; |
1159 |
for (int j = 0; j < IRRADIANCE_TEXTURE_HEIGHT; ++j) { |
1160 |
for (int i = 0; i < IRRADIANCE_TEXTURE_WIDTH; ++i) { |
1161 |
double r, mu_s; |
1162 |
float result[NSSAMP] = {0}; |
1163 |
const double xr = (double)i / IRRADIANCE_TEXTURE_WIDTH; |
1164 |
const double yr = (double)j / IRRADIANCE_TEXTURE_HEIGHT; |
1165 |
from_irradiance_uv(xr, yr, &r, &mu_s); |
1166 |
compute_direct_irradiance(tau_dp, r, mu_s, result); |
1167 |
for (int k = 0; k < NSSAMP; ++k) { |
1168 |
delta_irradiance_dp->arr.d[idx] = result[k]; |
1169 |
irradiance_dp->arr.d[idx] = 0.0; |
1170 |
idx += 1; |
1171 |
} |
1172 |
} |
1173 |
} |
1174 |
|
1175 |
/* Computing single scattering... */ |
1176 |
THREAD *threads = (THREAD *)malloc(num_threads * sizeof(THREAD)); |
1177 |
Scat1Tdat *tdata = (Scat1Tdat *)malloc(num_threads * sizeof(Scat1Tdat)); |
1178 |
for (int i = 0; i < num_threads; i++) { |
1179 |
tdata[i].atmos = atmos; |
1180 |
tdata[i].tau_dp = tau_dp; |
1181 |
tdata[i].delta_rayleigh_scattering_dp = delta_rayleigh_scattering_dp; |
1182 |
tdata[i].delta_mie_scattering_dp = delta_mie_scattering_dp; |
1183 |
tdata[i].scattering_dp = scattering_dp; |
1184 |
tdata[i].start_l = i * tchunk; |
1185 |
tdata[i].end_l = (i + 1) * tchunk; |
1186 |
if (i == num_threads - 1) |
1187 |
tdata[i].end_l += tremainder; |
1188 |
CREATE_THREAD(&threads[i], compute_single_scattering_thread, |
1189 |
(void *)&tdata[i]); |
1190 |
} |
1191 |
|
1192 |
for (int i = 0; i < num_threads; i++) { |
1193 |
THREAD_JOIN(threads[i]); |
1194 |
#if defined(_WIN32) || defined(_WIN64) |
1195 |
THREAD_CLOSE(threads[i]); |
1196 |
#endif |
1197 |
} |
1198 |
free(tdata); |
1199 |
free(threads); |
1200 |
savedata(delta_mie_scattering_dp); |
1201 |
|
1202 |
|
1203 |
for (int scattering_order = 2; scattering_order <= sorder; ++scattering_order) { |
1204 |
THREAD *threads = (THREAD *)malloc(num_threads * sizeof(THREAD)); |
1205 |
ScatDenseTdat *tdata = (ScatDenseTdat *)malloc(num_threads * sizeof(ScatDenseTdat)); |
1206 |
for (int i = 0; i < num_threads; i++) { |
1207 |
tdata[i].atmos = atmos; |
1208 |
tdata[i].tau_dp = tau_dp; |
1209 |
tdata[i].delta_rayleigh_scattering_dp = delta_rayleigh_scattering_dp; |
1210 |
tdata[i].delta_mie_scattering_dp = delta_mie_scattering_dp; |
1211 |
tdata[i].delta_multiple_scattering_dp = delta_multiple_scattering_dp; |
1212 |
tdata[i].delta_irradiance_dp = delta_irradiance_dp; |
1213 |
tdata[i].scattering_order = scattering_order; |
1214 |
tdata[i].delta_scattering_density_dp = delta_scattering_density_dp; |
1215 |
tdata[i].start_l = i * tchunk; |
1216 |
tdata[i].end_l = (i + 1) * tchunk; |
1217 |
if (i == num_threads - 1) |
1218 |
tdata[i].end_l += tremainder; |
1219 |
CREATE_THREAD(&threads[i], compute_scattering_density_thread, |
1220 |
(void *)&tdata[i]); |
1221 |
} |
1222 |
|
1223 |
for (int i = 0; i < num_threads; i++) { |
1224 |
THREAD_JOIN(threads[i]); |
1225 |
#if defined(_WIN32) || defined(_WIN64) |
1226 |
THREAD_CLOSE(threads[i]); |
1227 |
#endif |
1228 |
} |
1229 |
free(tdata); |
1230 |
free(threads); |
1231 |
|
1232 |
idx = 0; |
1233 |
for (unsigned int j = 0; j < IRRADIANCE_TEXTURE_HEIGHT; ++j) { |
1234 |
for (unsigned int i = 0; i < IRRADIANCE_TEXTURE_WIDTH; ++i) { |
1235 |
double delta_irradiance[NSSAMP] = {0}; |
1236 |
double r, mu_s; |
1237 |
const double xr = (double)i / IRRADIANCE_TEXTURE_WIDTH; |
1238 |
const double yr = (double)j / IRRADIANCE_TEXTURE_HEIGHT; |
1239 |
from_irradiance_uv(xr, yr, &r, &mu_s); |
1240 |
compute_indirect_irradiance(delta_rayleigh_scattering_dp, |
1241 |
delta_mie_scattering_dp, |
1242 |
delta_multiple_scattering_dp, r, mu_s, |
1243 |
scattering_order - 1, delta_irradiance); |
1244 |
for (int k = 0; k < NSSAMP; ++k) { |
1245 |
delta_irradiance_dp->arr.d[idx] = delta_irradiance[k]; |
1246 |
idx += 1; |
1247 |
} |
1248 |
} |
1249 |
} |
1250 |
|
1251 |
increment_dp(irradiance_dp, delta_irradiance_dp); |
1252 |
|
1253 |
THREAD *threads2 = (THREAD *)malloc(num_threads * sizeof(THREAD)); |
1254 |
ScatNTdat *tdata2 = (ScatNTdat *)malloc(num_threads * sizeof(ScatNTdat)); |
1255 |
for (int i = 0; i < num_threads; i++) { |
1256 |
tdata2[i].tau_dp = tau_dp; |
1257 |
tdata2[i].delta_multiple_scattering_dp = delta_multiple_scattering_dp; |
1258 |
tdata2[i].delta_scattering_density_dp = delta_scattering_density_dp; |
1259 |
tdata2[i].scattering_dp = scattering_dp; |
1260 |
tdata2[i].start_l = i * tchunk; |
1261 |
tdata2[i].end_l = (i + 1) * tchunk; |
1262 |
if (i == num_threads - 1) |
1263 |
tdata2[i].end_l += tremainder; |
1264 |
CREATE_THREAD(&threads2[i], compute_multi_scattering_thread, |
1265 |
(void *)&tdata2[i]); |
1266 |
} |
1267 |
|
1268 |
for (int i = 0; i < num_threads; i++) { |
1269 |
THREAD_JOIN(threads2[i]); |
1270 |
#if defined(_WIN32) || defined(_WIN64) |
1271 |
THREAD_CLOSE(threads2[i]); |
1272 |
#endif |
1273 |
} |
1274 |
free(tdata2); |
1275 |
free(threads2); |
1276 |
} |
1277 |
savedata(scattering_dp); |
1278 |
savedata(irradiance_dp); |
1279 |
|
1280 |
freedata(tau_dp); |
1281 |
freedata(delta_rayleigh_scattering_dp); |
1282 |
freedata(delta_mie_scattering_dp); |
1283 |
freedata(scattering_dp); |
1284 |
freedata(delta_multiple_scattering_dp); |
1285 |
freedata(delta_scattering_density_dp); |
1286 |
freedata(delta_irradiance_dp); |
1287 |
freedata(irradiance_dp); |
1288 |
return 1; |
1289 |
} |
1290 |
|
1291 |
int compute_sundir(const int year, const int month, const int day, |
1292 |
const double hour, const int tsolar, double sundir[3]) { |
1293 |
double sd, st = hour; |
1294 |
|
1295 |
if (year) { |
1296 |
/* Michalsky algorithm? */ |
1297 |
double mjd = mjdate(year, month, day, hour); |
1298 |
if (tsolar) |
1299 |
sd = msdec(mjd, NULL); |
1300 |
else |
1301 |
sd = msdec(mjd, &st); |
1302 |
} else { |
1303 |
int jd = jdate(month, day); /* Julian date */ |
1304 |
sd = sdec(jd); /* solar declination */ |
1305 |
if (!tsolar) /* get solar time? */ |
1306 |
st = hour + stadj(jd); |
1307 |
} |
1308 |
const double altitude = salt(sd, st); |
1309 |
const double azimuth = sazi(sd, st); |
1310 |
sundir[0] = -sin(azimuth) * cos(altitude); |
1311 |
sundir[1] = -cos(azimuth) * cos(altitude); |
1312 |
sundir[2] = sin(altitude); |
1313 |
return 1; |
1314 |
} |
1315 |
|
1316 |
void get_sky_transmittance(DATARRAY *tau, const double radius, const double mu, float *result) { |
1317 |
DATARRAY *trans = get_transmittance_to_space(tau, radius, mu); |
1318 |
for (int i = 0; i < NSSAMP; ++i) { |
1319 |
result[i] = trans->arr.d[i]; |
1320 |
} |
1321 |
free(trans); |
1322 |
} |
1323 |
|
1324 |
void get_sky_radiance(DATARRAY *scat, DATARRAY *scat1m, const double radius, |
1325 |
const double mu, const double mu_s, const double nu, float *result) { |
1326 |
double u, v, w, z; |
1327 |
to_scattering_uvwz(radius, mu, mu_s, nu, 0, &u, &v, &w, &z); |
1328 |
double pt[4] = {z, w, v, u}; |
1329 |
|
1330 |
DATARRAY *scattering = datavector(scat, pt); |
1331 |
DATARRAY *single_mie_scattering = datavector(scat1m, pt); |
1332 |
|
1333 |
const double rayleigh_phase = rayleigh_phase_function(nu); |
1334 |
const double mie_phase = mie_phase_function(MIE_G, nu); |
1335 |
|
1336 |
for (int i = 0; i < NSSAMP; ++i) { |
1337 |
result[i] = scattering->arr.d[i] * rayleigh_phase + |
1338 |
single_mie_scattering->arr.d[i] * mie_phase; |
1339 |
} |
1340 |
free(single_mie_scattering); |
1341 |
free(scattering); |
1342 |
} |
1343 |
|
1344 |
static void get_irradiance(DATARRAY *tau, DATARRAY *irrad, const double radius, |
1345 |
const FVECT point, const FVECT normal, |
1346 |
const FVECT sundir, double *result) { |
1347 |
double mu_s = fdot(point, sundir) / radius; |
1348 |
|
1349 |
double point_trans_sun[NSSAMP] = {0}; |
1350 |
get_transmittance_to_sun(tau, radius, mu_s, point_trans_sun); |
1351 |
|
1352 |
DATARRAY *indirect_irradiance = get_indirect_irradiance(irrad, radius, mu_s); |
1353 |
const double sun_ct = fmax(fdot(normal, sundir), 0.0); |
1354 |
|
1355 |
for (int i = 0; i < NSSAMP; ++i) { |
1356 |
result[i] = indirect_irradiance->arr.d[i] + point_trans_sun[i] * EXTSOL[i] * sun_ct; |
1357 |
} |
1358 |
|
1359 |
free(indirect_irradiance); |
1360 |
} |
1361 |
|
1362 |
|
1363 |
void get_ground_radiance(DATARRAY *tau, DATARRAY *scat, DATARRAY *scat1m, DATARRAY *irrad, |
1364 |
const FVECT view_point, const FVECT view_direction, |
1365 |
const double radius, const double mu, const double sun_ct, |
1366 |
const double nu, const double grefl, const FVECT sundir, |
1367 |
float *ground_radiance) { |
1368 |
|
1369 |
const int intersect = ray_hits_ground(radius, mu); |
1370 |
|
1371 |
if (intersect) { |
1372 |
FVECT point, normal; |
1373 |
const double distance = distance_to_earth(radius, mu); |
1374 |
|
1375 |
/* direct + indirect irradiance */ |
1376 |
VSUM(point, view_point, view_direction, distance); |
1377 |
VCOPY(normal, point); |
1378 |
normalize(normal); |
1379 |
double irradiance[NSSAMP] = {0}; |
1380 |
get_irradiance(tau, irrad, ER, point, normal, sundir, irradiance); |
1381 |
|
1382 |
/* transmittance between view point and ground point */ |
1383 |
double trans[NSSAMP] = {0}; |
1384 |
get_transmittance(tau, radius, mu, distance, intersect, trans); |
1385 |
if (trans[0] == 0) {printf("trans 0\n");} |
1386 |
|
1387 |
/* inscattering */ |
1388 |
float inscatter[NSSAMP] = {0}; |
1389 |
get_sky_radiance(scat, scat1m, radius, mu, sun_ct, nu, inscatter); |
1390 |
|
1391 |
for (int i = 0; i < NSSAMP; ++i) { |
1392 |
ground_radiance[i] = inscatter[i] + irradiance[i] * trans[i] * grefl / PI; |
1393 |
} |
1394 |
} |
1395 |
} |
1396 |
|
1397 |
void get_solar_radiance(DATARRAY *tau, DATARRAY *scat, DATARRAY *scat1m, const FVECT sundir, const double radius, const double sun_ct, double *sun_radiance) { |
1398 |
double trans_sun[NSSAMP] = {0}; |
1399 |
float sky_radiance_sun[NSSAMP] = {0}; |
1400 |
double u, v, w, z; |
1401 |
const double nu = fdot(sundir, sundir); |
1402 |
get_transmittance_to_sun(tau, radius, sun_ct, trans_sun); |
1403 |
get_sky_radiance(scat, scat1m, radius, sun_ct, sun_ct, nu, sky_radiance_sun); |
1404 |
for (int i = 0; i < NSSAMP; ++i) { |
1405 |
sun_radiance[i] = sky_radiance_sun[i] + trans_sun[i] * EXTSOL[i] / SOLOMG; |
1406 |
} |
1407 |
} |