ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/atmos.c
Revision: 2.1
Committed: Fri Jul 5 18:04:36 2024 UTC (9 months, 3 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
feat(genssky): Taoning Wang added utility for generating spectral skies

File Contents

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