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

# User Rev Content
1 greg 2.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     }