htrdr

Solving radiative transfer in heterogeneous media
git clone git://git.meso-star.fr/htrdr.git
Log | Files | Refs | README | LICENSE

commit 4abbb2e19283261047025c0b9e48586da2bd3fc3
parent 6d9ea2c72a6d274fcf20463c13ba77f9221c0800
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 15 Apr 2020 10:13:51 +0200

Update the sampling of the spectral dimension in short wave

First sample a wavelength wrt the CIE 1931 XYZ color space. Then define
its associated spectral band and finally sample a quadrature point into
it.

Diffstat:
Mcmake/CMakeLists.txt | 4++--
Asrc/:w | 457+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/htrdr.c | 8++++++++
Msrc/htrdr.h | 2++
Dsrc/htrdr_CIE_XYZ.c | 283-------------------------------------------------------------------------------
Dsrc/htrdr_CIE_XYZ.h | 61-------------------------------------------------------------
Asrc/htrdr_cie_xyz.c | 284+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/htrdr_cie_xyz.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/htrdr_compute_radiance_sw.c | 12+++++-------
Msrc/htrdr_draw_radiance.c | 26+++++++++++---------------
Msrc/htrdr_solve.h | 1+
11 files changed, 831 insertions(+), 368 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -90,8 +90,8 @@ set(HTRDR_FILES_SRC htrdr.c htrdr_args.c htrdr_buffer.c - htrdr_CIE_XYZ.c htrdr_camera.c + htrdr_cie_xyz.c htrdr_compute_radiance_sw.c htrdr_compute_radiance_lw.c htrdr_draw_radiance.c @@ -107,8 +107,8 @@ set(HTRDR_FILES_INC htrdr.h htrdr_c.h htrdr_buffer.h - htrdr_CIE_XYZ.h htrdr_camera.h + htrdr_cie_xyz.h htrdr_grid.h htrdr_ground.h htrdr_interface.h diff --git a/src/:w b/src/:w @@ -0,0 +1,457 @@ +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "htrdr.h" +#include "htrdr_c.h" +#include "htrdr_interface.h" +#include "htrdr_ground.h" +#include "htrdr_solve.h" +#include "htrdr_sun.h" + +#include <high_tune/htsky.h> + +#include <star/s3d.h> +#include <star/ssf.h> +#include <star/ssp.h> +#include <star/svx.h> + +#include <rsys/double2.h> +#include <rsys/float2.h> +#include <rsys/float3.h> + +struct scattering_context { + struct ssp_rng* rng; + const struct htsky* sky; + size_t iband; /* Index of the spectral band */ + size_t iquad; /* Index of the quadrature point into the band */ + + double Ts; /* Sampled optical thickness */ + double traversal_dst; /* Distance traversed along the ray */ +}; +static const struct scattering_context SCATTERING_CONTEXT_NULL = { + NULL, NULL, 0, 0, 0, 0 +}; + +struct transmissivity_context { + struct ssp_rng* rng; + const struct htsky* sky; + size_t iband; /* Index of the spectrald */ + size_t iquad; /* Index of the quadrature point into the band */ + + double Ts; /* Sampled optical thickness */ + double Tmin; /* Minimal optical thickness */ + double traversal_dst; /* Distance traversed along the ray */ + + enum htsky_property prop; +}; +static const struct transmissivity_context TRANSMISSION_CONTEXT_NULL = { + NULL, NULL, 0, 0, 0, 0, 0, 0 +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static int +scattering_hit_filter + (const struct svx_hit* hit, + const double org[3], + const double dir[3], + const double range[2], + void* context) +{ + struct scattering_context* ctx = context; + double ks_max; + int pursue_traversal = 1; + ASSERT(hit && ctx && !SVX_HIT_NONE(hit) && org && dir && range); + (void)range; + + ks_max = htsky_fetch_svx_voxel_property(ctx->sky, HTSKY_Ks, + HTSKY_SVX_MAX, HTSKY_CPNT_MASK_ALL, ctx->iband, ctx->iquad, &hit->voxel); + + ctx->traversal_dst = hit->distance[0]; + + /* Iterate until a collision occurs into the voxel or until the ray + * does not collide the voxel */ + for(;;) { + /* Compute tau for the current leaf */ + const double vox_dst = hit->distance[1] - ctx->traversal_dst; + const double T = vox_dst * ks_max; + + /* A collision occurs behind `vox_dst' */ + if(ctx->Ts > T) { + ctx->Ts -= T; + ctx->traversal_dst = hit->distance[1]; + pursue_traversal = 1; + break; + + /* A real/null collision occurs before `vox_dst' */ + } else { + double pos[3]; + double proba; + double ks; + const double collision_dst = ctx->Ts / ks_max; + + /* Compute the traversed distance up to the challenged collision */ + ctx->traversal_dst += collision_dst; + ASSERT(ctx->traversal_dst >= hit->distance[0]); + ASSERT(ctx->traversal_dst <= hit->distance[1]); + + /* Compute the world space position where a collision may occur */ + pos[0] = org[0] + ctx->traversal_dst * dir[0]; + pos[1] = org[1] + ctx->traversal_dst * dir[1]; + pos[2] = org[2] + ctx->traversal_dst * dir[2]; + + ks = htsky_fetch_raw_property(ctx->sky, HTSKY_Ks, + HTSKY_CPNT_MASK_ALL, ctx->iband, ctx->iquad, pos, -DBL_MAX, DBL_MAX); + + /* Handle the case that ks_max is not *really* the max */ + proba = ks / ks_max; + + if(ssp_rng_canonical(ctx->rng) < proba) {/* Collide <=> real scattering */ + pursue_traversal = 0; + break; + } else { /* Null collision */ + ctx->Ts = ssp_ran_exp(ctx->rng, 1); /* Sample a new optical thickness */ + } + } + } + return pursue_traversal; +} + +static int +transmissivity_hit_filter + (const struct svx_hit* hit, + const double org[3], + const double dir[3], + const double range[2], + void* context) +{ + struct transmissivity_context* ctx = context; + int comp_mask = HTSKY_CPNT_MASK_ALL; + double k_max; + double k_min; + int pursue_traversal = 1; + ASSERT(hit && ctx && !SVX_HIT_NONE(hit) && org && dir && range); + (void)range; + + k_min = htsky_fetch_svx_voxel_property(ctx->sky, ctx->prop, + HTSKY_SVX_MIN, comp_mask, ctx->iband, ctx->iquad, &hit->voxel); + k_max = htsky_fetch_svx_voxel_property(ctx->sky, ctx->prop, + HTSKY_SVX_MAX, comp_mask, ctx->iband, ctx->iquad, &hit->voxel); + ASSERT(k_min <= k_max); + + ctx->Tmin += (hit->distance[1] - hit->distance[0]) * k_min; + ctx->traversal_dst = hit->distance[0]; + + /* Iterate until a collision occurs into the voxel or until the ray + * does not collide the voxel */ + for(;;) { + const double vox_dst = hit->distance[1] - ctx->traversal_dst; + const double Tdif = vox_dst * (k_max-k_min); + + /* A collision occurs behind `vox_dst' */ + if(ctx->Ts > Tdif) { + ctx->Ts -= Tdif; + ctx->traversal_dst = hit->distance[1]; + pursue_traversal = 1; + break; + + /* A real/null collision occurs before `vox_dst' */ + } else { + double x[3]; + double k; + double proba; + double collision_dst = ctx->Ts / (k_max - k_min); + + /* Compute the traversed distance up to the challenged collision */ + ctx->traversal_dst += collision_dst; + ASSERT(ctx->traversal_dst >= hit->distance[0]); + ASSERT(ctx->traversal_dst <= hit->distance[1]); + + /* Compute the world space position where a collision may occur */ + x[0] = org[0] + ctx->traversal_dst * dir[0]; + x[1] = org[1] + ctx->traversal_dst * dir[1]; + x[2] = org[2] + ctx->traversal_dst * dir[2]; + + k = htsky_fetch_raw_property(ctx->sky, ctx->prop, + comp_mask, ctx->iband, ctx->iquad, x, k_min, k_max); + ASSERT(k >= k_min && k <= k_max); + + proba = (k - k_min) / (k_max - k_min); + + if(ssp_rng_canonical(ctx->rng) < proba) { /* Collide */ + pursue_traversal = 0; + break; + } else { /* Null collision */ + ctx->Ts = ssp_ran_exp(ctx->rng, 1); /* Sample a new optical thickness */ + } + } + } + return pursue_traversal; +} + +static double +transmissivity + (struct htrdr* htrdr, + struct ssp_rng* rng, + const enum htsky_property prop, + const size_t iband, + const size_t iquad, + const double pos[3], + const double dir[3], + const double range[2]) +{ + struct svx_hit svx_hit; + struct transmissivity_context transmissivity_ctx = TRANSMISSION_CONTEXT_NULL; + + ASSERT(htrdr && rng && pos && dir && range); + + transmissivity_ctx.rng = rng; + transmissivity_ctx.sky = htrdr->sky; + transmissivity_ctx.iband = iband; + transmissivity_ctx.iquad = iquad; + transmissivity_ctx.Ts = ssp_ran_exp(rng, 1); /* Sample an optical thickness */ + transmissivity_ctx.prop = prop; + + /* Compute the transmissivity */ + HTSKY(trace_ray(htrdr->sky, pos, dir, range, NULL, + transmissivity_hit_filter, &transmissivity_ctx, iband, iquad, &svx_hit)); + + if(SVX_HIT_NONE(&svx_hit)) { + return transmissivity_ctx.Tmin ? exp(-transmissivity_ctx.Tmin) : 1.0; + } else { + return 0; + } +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +double +htrdr_compute_radiance_sw + (struct htrdr* htrdr, + const size_t ithread, + struct ssp_rng* rng, + const double pos_in[3], + const double dir_in[3], + const double wlen, /* In nanometer */ + const size_t iband, + const size_t iquad) +{ + struct s3d_hit s3d_hit = S3D_HIT_NULL; + struct s3d_hit s3d_hit_tmp = S3D_HIT_NULL; + struct s3d_hit s3d_hit_prev = S3D_HIT_NULL; + struct svx_hit svx_hit = SVX_HIT_NULL; + struct ssf_phase* phase_hg = NULL; + struct ssf_phase* phase_rayleigh = NULL; + + double pos[3]; + double dir[3]; + double range[2]; + double pos_next[3]; + double dir_next[3]; + double band_bounds[2]; /* In nanometers */ + + double wlen; + double R; + double r; /* Random number */ + double wo[3]; /* -dir */ + double pdf; + double sun_solid_angle; + double Tr; /* Overall transmissivity */ + double Tr_abs; /* Absorption transmissivity */ + double L_sun; /* Sun radiance in W.m^-2.sr^-1 */ + double sun_dir[3]; + double ksi = 1; /* Throughput */ + double w = 0; /* MC weight */ + double g = 0; /* Asymmetry parameter of the HG phase function */ + + ASSERT(htrdr && rng && pos_in && dir_in && ithread < htrdr->nthreads); + ASSERT(!htsky_is_long_wave(htrdr->sky)); + + CHK(RES_OK == ssf_phase_create + (&htrdr->lifo_allocators[ithread], &ssf_phase_hg, &phase_hg)); + CHK(RES_OK == ssf_phase_create + (&htrdr->lifo_allocators[ithread], &ssf_phase_rayleigh, &phase_rayleigh)); + + /* Setup the phase function for this spectral band & quadrature point */ + g = htsky_fetch_particle_phase_function_asymmetry_parameter + (htrdr->sky, iband, iquad); + SSF(phase_hg_setup(phase_hg, g)); + + /* Fetch sun properties. Note that the sun spectral data are defined by bands + * that, actually are the same of the SW spectral bands defined in the + * default "ecrad_opt_prot.txt" file provided by the HTGOP project. */ + htsky_get_spectral_band_bounds(htrdr->sky, iband, band_bounds); + ASSERT(band_bounds[0] <= wlen && wlen <= band_bounds[1]); + sun_solid_angle = htrdr_sun_get_solid_angle(htrdr->sun); + L_sun = htrdr_sun_get_radiance(htrdr->sun, wlen); + + d3_set(pos, pos_in); + d3_set(dir, dir_in); + + /* Add the directly contribution of the sun */ + if(htrdr_sun_is_dir_in_solar_cone(htrdr->sun, dir)) { + /* Add the direct contribution of the sun */ + d2(range, 0, FLT_MAX); + + /* Check that the ray is not occlude along the submitted range */ + HTRDR(ground_trace_ray(htrdr->ground, pos, dir, range, NULL, &s3d_hit_tmp)); + if(!S3D_HIT_NONE(&s3d_hit_tmp)) { + Tr = 0; + } else { + Tr = transmissivity + (htrdr, rng, HTSKY_Kext, iband, iquad , pos, dir, range); + w = L_sun * Tr; + } + } + + /* Radiative random walk */ + for(;;) { + struct scattering_context scattering_ctx = SCATTERING_CONTEXT_NULL; + double bounce_reflectivity = 1; + + /* Find the first intersection with a surface */ + d2(range, 0, DBL_MAX); + HTRDR(ground_trace_ray + (htrdr->ground, pos, dir, range, &s3d_hit_prev, &s3d_hit)); + + /* Sample an optical thickness */ + scattering_ctx.Ts = ssp_ran_exp(rng, 1); + + /* Setup the remaining scattering context fields */ + scattering_ctx.rng = rng; + scattering_ctx.sky = htrdr->sky; + scattering_ctx.iband = iband; + scattering_ctx.iquad = iquad; + + /* Define if a scattering event occurs */ + d2(range, 0, s3d_hit.distance); + HTSKY(trace_ray(htrdr->sky, pos, dir, range, NULL, + scattering_hit_filter, &scattering_ctx, iband, iquad, &svx_hit)); + + /* No scattering and no surface reflection. Stop the radiative random walk */ + if(S3D_HIT_NONE(&s3d_hit) && SVX_HIT_NONE(&svx_hit)) { + break; + } + ASSERT(SVX_HIT_NONE(&svx_hit) + || ( svx_hit.distance[0] <= scattering_ctx.traversal_dst + && svx_hit.distance[1] >= scattering_ctx.traversal_dst)); + + /* Negate the incoming dir to match the convention of the SSF library */ + d3_minus(wo, dir); + + /* Compute the new position */ + pos_next[0] = pos[0] + dir[0]*scattering_ctx.traversal_dst; + pos_next[1] = pos[1] + dir[1]*scattering_ctx.traversal_dst; + pos_next[2] = pos[2] + dir[2]*scattering_ctx.traversal_dst; + + /* Define the absorption transmissivity from the current position to the + * next position */ + d2(range, 0, scattering_ctx.traversal_dst); + Tr_abs = transmissivity + (htrdr, rng, HTSKY_Ka, iband, iquad, pos, dir, range); + if(Tr_abs <= 0) break; + + /* Sample a sun direction */ + htrdr_sun_sample_direction(htrdr->sun, rng, sun_dir); + d2(range, 0, FLT_MAX); + + s3d_hit_prev = SVX_HIT_NONE(&svx_hit) ? s3d_hit : S3D_HIT_NULL; + + /* Check that the sun is visible from the new position */ + HTRDR(ground_trace_ray + (htrdr->ground, pos_next, sun_dir, range, &s3d_hit_prev, &s3d_hit_tmp)); + + /* Compute the sun transmissivity */ + if(!S3D_HIT_NONE(&s3d_hit_tmp)) { + Tr = 0; + } else { + Tr = transmissivity + (htrdr, rng, HTSKY_Kext, iband, iquad, pos_next, sun_dir, range); + } + + /* Scattering at a surface */ + if(SVX_HIT_NONE(&svx_hit)) { + struct htrdr_interface interf = HTRDR_INTERFACE_NULL; + struct ssf_bsdf* bsdf = NULL; + double N[3]; + int type; + + /* Fetch the hit interface and build its BSDF */ + htrdr_ground_get_interface(htrdr->ground, &s3d_hit, &interf); + HTRDR(interface_create_bsdf + (htrdr, &interf, ithread, wlen, pos_next, dir, rng, &s3d_hit, &bsdf)); + + d3_normalize(N, d3_set_f3(N, s3d_hit.normal)); + if(d3_dot(N, wo) < 0) d3_minus(N, N); + + bounce_reflectivity = ssf_bsdf_sample + (bsdf, rng, wo, N, dir_next, &type, &pdf); + if(!(type & SSF_REFLECTION)) { /* Handle only reflections */ + bounce_reflectivity = 0; + } + + if(d3_dot(N, sun_dir) < 0) { /* Below the ground */ + R = 0; + } else { + R = ssf_bsdf_eval(bsdf, wo, N, sun_dir) * d3_dot(N, sun_dir); + } + + /* Release the BSDF */ + SSF(bsdf_ref_put(bsdf)); + + /* Scattering in the medium */ + } else { + struct ssf_phase* phase; + double ks_particle; /* Scattering coefficient of the particles */ + double ks_gas; /* Scattering coefficient of the gaz */ + double ks; /* Overall scattering coefficient */ + + ks_gas = htsky_fetch_raw_property(htrdr->sky, HTSKY_Ks, + HTSKY_CPNT_FLAG_GAS, iband, iquad, pos_next, -DBL_MAX, DBL_MAX); + ks_particle = htsky_fetch_raw_property(htrdr->sky, HTSKY_Ks, + HTSKY_CPNT_FLAG_PARTICLES, iband, iquad, pos_next, -DBL_MAX, DBL_MAX); + ks = ks_particle + ks_gas; + + r = ssp_rng_canonical(rng); + if(r < ks_gas / ks) { /* Gas scattering */ + phase = phase_rayleigh; + } else { /* Cloud scattering */ + phase = phase_hg; + } + + ssf_phase_sample(phase, rng, wo, dir_next, NULL); + R = ssf_phase_eval(phase, wo, sun_dir); + } + + /* Update the MC weight */ + ksi *= Tr_abs; + w += ksi * L_sun * sun_solid_angle * Tr * R; + + /* Russian roulette wrt surface scattering */ + if(SVX_HIT_NONE(&svx_hit) && ssp_rng_canonical(rng) >= bounce_reflectivity) + break; + + /* Setup the next random walk state */ + d3_set(pos, pos_next); + d3_set(dir, dir_next); + } + SSF(phase_ref_put(phase_hg)); + SSF(phase_ref_put(phase_rayleigh)); + return w; +} + diff --git a/src/htrdr.c b/src/htrdr.c @@ -20,6 +20,7 @@ #include "htrdr_c.h" #include "htrdr_args.h" #include "htrdr_buffer.h" +#include "htrdr_cie_xyz.h" #include "htrdr_camera.h" #include "htrdr_ground.h" #include "htrdr_mtl.h" @@ -583,6 +584,12 @@ htrdr_init /* Define the CDF used to sample a long wave band */ res = setup_lw_cdf(htrdr); if(res != RES_OK) goto error; + } else { + /* Setup the CDF used to sample the short wave */ + res = htrdr_cie_xyz_create + (htrdr, HTRDR_CIE_XYZ_RANGE_DEFAULT, 400, &htrdr->cie); + if(res != RES_OK) goto error; + } htrdr->lifo_allocators = MEM_CALLOC @@ -648,6 +655,7 @@ htrdr_release(struct htrdr* htrdr) if(htrdr->cam) htrdr_camera_ref_put(htrdr->cam); if(htrdr->buf) htrdr_buffer_ref_put(htrdr->buf); if(htrdr->mtl) htrdr_mtl_ref_put(htrdr->mtl); + if(htrdr->cie) htrdr_cie_xyz_ref_put(htrdr->cie); if(htrdr->output && htrdr->output != stdout) fclose(htrdr->output); if(htrdr->lifo_allocators) { size_t i; diff --git a/src/htrdr.h b/src/htrdr.h @@ -35,6 +35,7 @@ struct htsky; struct htrdr_args; struct htrdr_buffer; +struct htrdr_cie_xyz; struct htrdr_rectangle; struct mem_allocator; struct mutext; @@ -49,6 +50,7 @@ struct htrdr { struct htrdr_ground* ground; struct htrdr_mtl* mtl; struct htrdr_sun* sun; + struct htrdr_cie_xyz* cie; struct htrdr_camera* cam; struct htrdr_buffer* buf; diff --git a/src/htrdr_CIE_XYZ.c b/src/htrdr_CIE_XYZ.c @@ -1,283 +0,0 @@ -/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#define _POSIX_C_SOURCE 200112L - -#include "htrdr.h" -#include "htrdr_c.h" -#include "htrdr_CIE_XYZ.h" - -#include <rsys/algorithm.h> -#include <rsys/cstr.h> -#include <rsys/dynamic_array_double.h> -#include <rsys/mem_allocator.h> -#include <rsys/ref_count.h> - -#include <math.h> /* nextafter */ - -struct htrdr_CIE_XYZ { - struct darray_double cdf_X; - struct darray_double cdf_Y; - struct darray_double cdf_Z; - double range[2]; /* Boundaries of the handled CIE XYZ color space */ - double band_len; /* Length in nanometers of a band */ - - struct htrdr* htrdr; - ref_T ref; -}; - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static INLINE double -trapezoidal_integration - (const double lambda_lo, /* Integral lower bound. In nanometer */ - const double lambda_hi, /* Integral upper bound. In nanometer */ - double (*f_bar)(const double lambda)) /* Function to integrate */ -{ - double dlambda; - size_t i, n; - double integral = 0; - ASSERT(lambda_lo <= lambda_hi); - ASSERT(lambda_lo > 0); - - n = (size_t)(lambda_hi - lambda_lo) + 1; - dlambda = (lambda_hi - lambda_lo) / (double)n; - - FOR_EACH(i, 0, n) { - const double lambda1 = lambda_lo + dlambda*(double)(i+0); - const double lambda2 = lambda_hi + dlambda*(double)(i+1); - const double f1 = f_bar(lambda1); - const double f2 = f_bar(lambda2); - integral += (f1 + f2)*dlambda*0.5; - } - return integral; -} - -/* The following 3 functions are used to fit the CIE Xbar, Ybar and Zbar curved - * has defined by the 1931 standard. These analytical fits are propsed by C. - * Wyman, P. P. Sloan & P. Shirley in "Simple Analytic Approximations to the - * CIE XYZ Color Matching Functions" - JCGT 2013. */ -static INLINE double -fit_x_bar_1931(const double lambda) -{ - const double a = (lambda - 442.0) * (lambda < 442.0 ? 0.0624 : 0.0374); - const double b = (lambda - 599.8) * (lambda < 599.8 ? 0.0264 : 0.0323); - const double c = (lambda - 501.1) * (lambda < 501.1 ? 0.0490 : 0.0382); - return 0.362*exp(-0.5*a*a) + 1.056*exp(-0.5f*b*b) - 0.065*exp(-0.5*c*c); -} - -static FINLINE double -fit_y_bar_1931(const double lambda) -{ - const double a = (lambda - 568.8) * (lambda < 568.8 ? 0.0213 : 0.0247); - const double b = (lambda - 530.9) * (lambda < 530.9 ? 0.0613 : 0.0322); - return 0.821*exp(-0.5*a*a) + 0.286*exp(-0.5*b*b); -} - -static FINLINE double -fit_z_bar_1931(const double lambda) -{ - const double a = (lambda - 437.0) * (lambda < 437.0 ? 0.0845 : 0.0278); - const double b = (lambda - 459.0) * (lambda < 459.0 ? 0.0385 : 0.0725); - return 1.217*exp(-0.5*a*a) + 0.681*exp(-0.5*b*b); -} - -static INLINE double -sample_CIE_XYZ - (const struct htrdr_CIE_XYZ* cie, - const double* cdf, - const size_t cdf_length, - const double r) /* Canonical number in [0, 1[ */ -{ - double r_next = nextafter(r, DBL_MAX); - double* find; - double wlen; - size_t i; - ASSERT(cie && cdf && cdf_length && r >= 0 && r < 1); - - /* Use r_next rather than r in order to find the first entry that is not less - * than *or equal* to r */ - find = search_lower_bound(&r_next, cdf, cdf_length, sizeof(double), cmp_dbl); - ASSERT(find); - - i = (size_t)(find - cdf); - ASSERT(i < cdf_length && cdf[i] > r && (!i || cdf[i-1] <= r)); - - /* Return the wavelength at the center of the sampled band */ - wlen = (double)i * cie->band_len + 0.5*cie->band_len; - ASSERT(cie->range[0] < wlen && wlen < cie->range[1]); - return wlen; -} - - -static void -release_CIE_XYZ(ref_T* ref) -{ - struct htrdr_CIE_XYZ* cie = NULL; - ASSERT(ref); - cie = CONTAINER_OF(ref, struct htrdr_CIE_XYZ, ref); - darray_double_release(&cie->cdf_X); - darray_double_release(&cie->cdf_Y); - darray_double_release(&cie->cdf_Z); -} - -/******************************************************************************* - * Local functions - ******************************************************************************/ -res_T -htrdr_CIE_XYZ_create - (struct htrdr* htrdr, - const double range[2], /* Must be included in [380, 780] nanometers */ - const size_t nbands, /* # bands used to discretisze the CIE tristimulus */ - struct htrdr_CIE_XYZ** out_cie) -{ - - enum { X, Y, Z }; /* Helper constant */ - struct htrdr_CIE_XYZ* cie = NULL; - double* pdf[3] = {NULL, NULL, NULL}; - double* cdf[3] = {NULL, NULL, NULL}; - double sum[3] = {0,0,0}; - size_t i; - res_T res = RES_OK; - ASSERT(htrdr && range && nbands && out_cie); - ASSERT(range[0] >= HTRDR_CIE_XYZ_RANGE_DEFAULT[0]); - ASSERT(range[1] <= HTRDR_CIE_XYZ_RANGE_DEFAULT[1]); - ASSERT(range[0] < range[1]); - - cie = MEM_CALLOC(htrdr->allocator, 1, sizeof(*cie)); - if(!cie) { - res = RES_MEM_ERR; - htrdr_log_err(htrdr, - "%s: could not allocate the CIE XYZ data structure -- %s.\n", - FUNC_NAME, res_to_cstr(res)); - goto error; - } - ref_init(&cie->ref); - cie->htrdr = htrdr; - cie->range[0] = range[0]; - cie->range[1] = range[1]; - darray_double_init(htrdr->allocator, &cie->cdf_X); - darray_double_init(htrdr->allocator, &cie->cdf_Y); - darray_double_init(htrdr->allocator, &cie->cdf_Z); - - /* Allocate and reset the memory space for the tristimulus CDF */ - #define SETUP_STIMULUS(Stimulus) { \ - res = darray_double_resize(&cie->cdf_ ## Stimulus, nbands); \ - if(res != RES_OK) { \ - htrdr_log_err(htrdr, \ - "%s: Could not reserve the memory space for the CDF " \ - "of the "STR(X)" stimulus -- %s.\n", FUNC_NAME, res_to_cstr(res)); \ - goto error; \ - } \ - cdf[Stimulus] = darray_double_data_get(&cie->cdf_ ## Stimulus); \ - pdf[Stimulus] = cdf[Stimulus]; \ - memset(cdf, 0, nbands*sizeof(double)); \ - } (void)0 - SETUP_STIMULUS(X); - SETUP_STIMULUS(Y); - SETUP_STIMULUS(Z); - #undef RESERVE - - /* Compute the *unormalized* pdf of the tristimulus */ - cie->band_len = (range[1] - range[0]) / (double)nbands; - FOR_EACH(i, 0, nbands) { - const double lambda_lo = range[0] + (double)i * cie->band_len; - const double lambda_hi = lambda_lo + cie->band_len; - ASSERT(lambda_lo <= lambda_hi); - pdf[X][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_x_bar_1931); - pdf[Y][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_y_bar_1931); - pdf[Z][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_z_bar_1931); - sum[X] += pdf[X][i]; - sum[Y] += pdf[Y][i]; - sum[Z] += pdf[Z][i]; - } - - FOR_EACH(i, 0, nbands) { - /* Normalize the pdf */ - pdf[X][i] /= sum[X]; - pdf[Y][i] /= sum[Y]; - pdf[Z][i] /= sum[Z]; - /* Setup the cumulative */ - if(i == 0) { - cdf[X][i] = pdf[X][i]; - cdf[Y][i] = pdf[Y][i]; - cdf[Z][i] = pdf[Z][i]; - } else { - cdf[X][i] = pdf[X][i] + cdf[X][i-1]; - cdf[Y][i] = pdf[Y][i] + cdf[Y][i-1]; - cdf[Z][i] = pdf[Z][i] + cdf[Z][i-1]; - } - } - ASSERT(eq_eps(cdf[X][nbands-1], 1, 1.e-6)); - ASSERT(eq_eps(cdf[Y][nbands-1], 1, 1.e-6)); - ASSERT(eq_eps(cdf[Z][nbands-1], 1, 1.e-6)); - -#ifndef NDEBUG - FOR_EACH(i, 1, nbands) { - ASSERT(cdf[X][i] >= cdf[X][i-1]); - ASSERT(cdf[Y][i] >= cdf[Y][i-1]); - ASSERT(cdf[Z][i] >= cdf[Z][i-1]); - } -#endif - - /* Handle numerical issue */ - cdf[X][nbands-1] = 1.0; - cdf[Y][nbands-1] = 1.0; - cdf[Z][nbands-1] = 1.0; - -exit: - *out_cie = cie; - return res; -error: - if(cie) htrdr_CIE_XYZ_ref_put(cie); - goto exit; -} - -void -htrdr_CIE_XYZ_ref_get(struct htrdr_CIE_XYZ* cie) -{ - ASSERT(cie); - ref_get(&cie->ref); -} - -void -htrdr_CIE_XYZ_ref_put(struct htrdr_CIE_XYZ* cie) -{ - ASSERT(cie); - ref_put(&cie->ref, release_CIE_XYZ); -} - -double -htrdr_CIE_XYZ_sample_X(struct htrdr_CIE_XYZ* cie, const double r) -{ - return sample_CIE_XYZ(cie, darray_double_cdata_get(&cie->cdf_X), - darray_double_size_get(&cie->cdf_X), r); -} - -double -htrdr_CIE_XYZ_sample_Y(struct htrdr_CIE_XYZ* cie, const double r) -{ - return sample_CIE_XYZ(cie, darray_double_cdata_get(&cie->cdf_Y), - darray_double_size_get(&cie->cdf_Y), r); -} - -double -htrdr_CIE_XYZ_sample_Z(struct htrdr_CIE_XYZ* cie, const double r) -{ - return sample_CIE_XYZ(cie, darray_double_cdata_get(&cie->cdf_Z), - darray_double_size_get(&cie->cdf_Z), r); -} - diff --git a/src/htrdr_CIE_XYZ.h b/src/htrdr_CIE_XYZ.h @@ -1,61 +0,0 @@ -/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#ifndef HTRDR_CIE_XYZ_H -#define HTRDR_CIE_XYZ_H - -#include <rsys/rsys.h> - -struct htrdr; -struct htrdr_CIE_XYZ; - -/* Wavelength boundaries of the CIE XYZ color space in nanometers */ -static const double HTRDR_CIE_XYZ_RANGE_DEFAULT[2] = {380, 780}; - -extern LOCAL_SYM res_T -htrdr_CIE_XYZ_create - (struct htrdr* htrdr, - const double range[2], /* Must be included in [380, 780] nanometers */ - const size_t nbands, /* # bands used to discretisze the CIE tristimulus s*/ - struct htrdr_CIE_XYZ** cie); - -extern LOCAL_SYM void -htrdr_CIE_XYZ_ref_get - (struct htrdr_CIE_XYZ* cie); - -extern LOCAL_SYM void -htrdr_CIE_XYZ_ref_put - (struct htrdr_CIE_XYZ* cie); - -/* Return a wavelength in nanometer */ -extern LOCAL_SYM double -htrdr_CIE_XYZ_sample_X - (struct htrdr_CIE_XYZ* cie, - const double r); /* Canonical number in [0, 1[ */ - -/* Return a wavelength in nanometer */ -extern LOCAL_SYM double -htrdr_CIE_XYZ_sample_Y - (struct htrdr_CIE_XYZ* cie, - const double r); /* Canonical number in [0, 1[ */ - -/* Return a wavelength in nanometer */ -extern LOCAL_SYM double -htrdr_CIE_XYZ_sample_Z - (struct htrdr_CIE_XYZ* cie, - const double r); /* Canonical number in [0, 1[ */ - -#endif /* HTRDR_CIE_XYZ_H */ - diff --git a/src/htrdr_cie_xyz.c b/src/htrdr_cie_xyz.c @@ -0,0 +1,284 @@ +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L + +#include "htrdr.h" +#include "htrdr_c.h" +#include "htrdr_cie_xyz.h" + +#include <rsys/algorithm.h> +#include <rsys/cstr.h> +#include <rsys/dynamic_array_double.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +#include <math.h> /* nextafter */ + +struct htrdr_cie_xyz { + struct darray_double cdf_X; + struct darray_double cdf_Y; + struct darray_double cdf_Z; + double range[2]; /* Boundaries of the handled CIE XYZ color space */ + double band_len; /* Length in nanometers of a band */ + + struct htrdr* htrdr; + ref_T ref; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE double +trapezoidal_integration + (const double lambda_lo, /* Integral lower bound. In nanometer */ + const double lambda_hi, /* Integral upper bound. In nanometer */ + double (*f_bar)(const double lambda)) /* Function to integrate */ +{ + double dlambda; + size_t i, n; + double integral = 0; + ASSERT(lambda_lo <= lambda_hi); + ASSERT(lambda_lo > 0); + + n = (size_t)(lambda_hi - lambda_lo) + 1; + dlambda = (lambda_hi - lambda_lo) / (double)n; + + FOR_EACH(i, 0, n) { + const double lambda1 = lambda_lo + dlambda*(double)(i+0); + const double lambda2 = lambda_hi + dlambda*(double)(i+1); + const double f1 = f_bar(lambda1); + const double f2 = f_bar(lambda2); + integral += (f1 + f2)*dlambda*0.5; + } + return integral; +} + +/* The following 3 functions are used to fit the CIE Xbar, Ybar and Zbar curved + * has defined by the 1931 standard. These analytical fits are propsed by C. + * Wyman, P. P. Sloan & P. Shirley in "Simple Analytic Approximations to the + * CIE XYZ Color Matching Functions" - JCGT 2013. */ +static INLINE double +fit_x_bar_1931(const double lambda) +{ + const double a = (lambda - 442.0) * (lambda < 442.0 ? 0.0624 : 0.0374); + const double b = (lambda - 599.8) * (lambda < 599.8 ? 0.0264 : 0.0323); + const double c = (lambda - 501.1) * (lambda < 501.1 ? 0.0490 : 0.0382); + return 0.362*exp(-0.5*a*a) + 1.056*exp(-0.5f*b*b) - 0.065*exp(-0.5*c*c); +} + +static FINLINE double +fit_y_bar_1931(const double lambda) +{ + const double a = (lambda - 568.8) * (lambda < 568.8 ? 0.0213 : 0.0247); + const double b = (lambda - 530.9) * (lambda < 530.9 ? 0.0613 : 0.0322); + return 0.821*exp(-0.5*a*a) + 0.286*exp(-0.5*b*b); +} + +static FINLINE double +fit_z_bar_1931(const double lambda) +{ + const double a = (lambda - 437.0) * (lambda < 437.0 ? 0.0845 : 0.0278); + const double b = (lambda - 459.0) * (lambda < 459.0 ? 0.0385 : 0.0725); + return 1.217*exp(-0.5*a*a) + 0.681*exp(-0.5*b*b); +} + +static INLINE double +sample_cie_xyz + (const struct htrdr_cie_xyz* cie, + const double* cdf, + const size_t cdf_length, + const double r) /* Canonical number in [0, 1[ */ +{ + double r_next = nextafter(r, DBL_MAX); + double* find; + double wlen; + size_t i; + ASSERT(cie && cdf && cdf_length && r >= 0 && r < 1); + + /* Use r_next rather than r in order to find the first entry that is not less + * than *or equal* to r */ + find = search_lower_bound(&r_next, cdf, cdf_length, sizeof(double), cmp_dbl); + ASSERT(find); + + i = (size_t)(find - cdf); + ASSERT(i < cdf_length && cdf[i] > r && (!i || cdf[i-1] <= r)); + + /* Return the wavelength at the center of the sampled band */ + wlen = cie->range[0] + cie->band_len * ((double)i + 0.5); + ASSERT(cie->range[0] < wlen && wlen < cie->range[1]); + return wlen; +} + + +static void +release_cie_xyz(ref_T* ref) +{ + struct htrdr_cie_xyz* cie = NULL; + ASSERT(ref); + cie = CONTAINER_OF(ref, struct htrdr_cie_xyz, ref); + darray_double_release(&cie->cdf_X); + darray_double_release(&cie->cdf_Y); + darray_double_release(&cie->cdf_Z); + MEM_RM(cie->htrdr->allocator, cie); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +htrdr_cie_xyz_create + (struct htrdr* htrdr, + const double range[2], /* Must be included in [380, 780] nanometers */ + const size_t nbands, /* # bands used to discretisze the CIE tristimulus */ + struct htrdr_cie_xyz** out_cie) +{ + + enum { X, Y, Z }; /* Helper constant */ + struct htrdr_cie_xyz* cie = NULL; + double* pdf[3] = {NULL, NULL, NULL}; + double* cdf[3] = {NULL, NULL, NULL}; + double sum[3] = {0,0,0}; + size_t i; + res_T res = RES_OK; + ASSERT(htrdr && range && nbands && out_cie); + ASSERT(range[0] >= HTRDR_CIE_XYZ_RANGE_DEFAULT[0]); + ASSERT(range[1] <= HTRDR_CIE_XYZ_RANGE_DEFAULT[1]); + ASSERT(range[0] < range[1]); + + cie = MEM_CALLOC(htrdr->allocator, 1, sizeof(*cie)); + if(!cie) { + res = RES_MEM_ERR; + htrdr_log_err(htrdr, + "%s: could not allocate the CIE XYZ data structure -- %s.\n", + FUNC_NAME, res_to_cstr(res)); + goto error; + } + ref_init(&cie->ref); + cie->htrdr = htrdr; + cie->range[0] = range[0]; + cie->range[1] = range[1]; + darray_double_init(htrdr->allocator, &cie->cdf_X); + darray_double_init(htrdr->allocator, &cie->cdf_Y); + darray_double_init(htrdr->allocator, &cie->cdf_Z); + + /* Allocate and reset the memory space for the tristimulus CDF */ + #define SETUP_STIMULUS(Stimulus) { \ + res = darray_double_resize(&cie->cdf_ ## Stimulus, nbands); \ + if(res != RES_OK) { \ + htrdr_log_err(htrdr, \ + "%s: Could not reserve the memory space for the CDF " \ + "of the "STR(X)" stimulus -- %s.\n", FUNC_NAME, res_to_cstr(res)); \ + goto error; \ + } \ + cdf[Stimulus] = darray_double_data_get(&cie->cdf_ ## Stimulus); \ + pdf[Stimulus] = cdf[Stimulus]; \ + memset(cdf[Stimulus], 0, nbands*sizeof(double)); \ + } (void)0 + SETUP_STIMULUS(X); + SETUP_STIMULUS(Y); + SETUP_STIMULUS(Z); + #undef SETUP_STIMULUS + + /* Compute the *unormalized* pdf of the tristimulus */ + cie->band_len = (range[1] - range[0]) / (double)nbands; + FOR_EACH(i, 0, nbands) { + const double lambda_lo = range[0] + (double)i * cie->band_len; + const double lambda_hi = lambda_lo + cie->band_len; + ASSERT(lambda_lo <= lambda_hi); + pdf[X][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_x_bar_1931); + pdf[Y][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_y_bar_1931); + pdf[Z][i] = trapezoidal_integration(lambda_lo, lambda_hi, fit_z_bar_1931); + sum[X] += pdf[X][i]; + sum[Y] += pdf[Y][i]; + sum[Z] += pdf[Z][i]; + } + + FOR_EACH(i, 0, nbands) { + /* Normalize the pdf */ + pdf[X][i] /= sum[X]; + pdf[Y][i] /= sum[Y]; + pdf[Z][i] /= sum[Z]; + /* Setup the cumulative */ + if(i == 0) { + cdf[X][i] = pdf[X][i]; + cdf[Y][i] = pdf[Y][i]; + cdf[Z][i] = pdf[Z][i]; + } else { + cdf[X][i] = pdf[X][i] + cdf[X][i-1]; + cdf[Y][i] = pdf[Y][i] + cdf[Y][i-1]; + cdf[Z][i] = pdf[Z][i] + cdf[Z][i-1]; + } + } + ASSERT(eq_eps(cdf[X][nbands-1], 1, 1.e-6)); + ASSERT(eq_eps(cdf[Y][nbands-1], 1, 1.e-6)); + ASSERT(eq_eps(cdf[Z][nbands-1], 1, 1.e-6)); + +#ifndef NDEBUG + FOR_EACH(i, 1, nbands) { + ASSERT(cdf[X][i] >= cdf[X][i-1]); + ASSERT(cdf[Y][i] >= cdf[Y][i-1]); + ASSERT(cdf[Z][i] >= cdf[Z][i-1]); + } +#endif + + /* Handle numerical issue */ + cdf[X][nbands-1] = 1.0; + cdf[Y][nbands-1] = 1.0; + cdf[Z][nbands-1] = 1.0; + +exit: + *out_cie = cie; + return res; +error: + if(cie) htrdr_cie_xyz_ref_put(cie); + goto exit; +} + +void +htrdr_cie_xyz_ref_get(struct htrdr_cie_xyz* cie) +{ + ASSERT(cie); + ref_get(&cie->ref); +} + +void +htrdr_cie_xyz_ref_put(struct htrdr_cie_xyz* cie) +{ + ASSERT(cie); + ref_put(&cie->ref, release_cie_xyz); +} + +double +htrdr_cie_xyz_sample_X(struct htrdr_cie_xyz* cie, const double r) +{ + return sample_cie_xyz(cie, darray_double_cdata_get(&cie->cdf_X), + darray_double_size_get(&cie->cdf_X), r); +} + +double +htrdr_cie_xyz_sample_Y(struct htrdr_cie_xyz* cie, const double r) +{ + return sample_cie_xyz(cie, darray_double_cdata_get(&cie->cdf_Y), + darray_double_size_get(&cie->cdf_Y), r); +} + +double +htrdr_cie_xyz_sample_Z(struct htrdr_cie_xyz* cie, const double r) +{ + return sample_cie_xyz(cie, darray_double_cdata_get(&cie->cdf_Z), + darray_double_size_get(&cie->cdf_Z), r); +} + diff --git a/src/htrdr_cie_xyz.h b/src/htrdr_cie_xyz.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef HTRDR_CIE_XYZ_H +#define HTRDR_CIE_XYZ_H + +#include <rsys/rsys.h> + +struct htrdr; +struct htrdr_cie_xyz; + +/* Wavelength boundaries of the CIE XYZ color space in nanometers */ +static const double HTRDR_CIE_XYZ_RANGE_DEFAULT[2] = {380, 780}; + +extern LOCAL_SYM res_T +htrdr_cie_xyz_create + (struct htrdr* htrdr, + const double range[2], /* Must be included in [380, 780] nanometers */ + const size_t nbands, /* # bands used to discretisze the CIE tristimulus s*/ + struct htrdr_cie_xyz** cie); + +extern LOCAL_SYM void +htrdr_cie_xyz_ref_get + (struct htrdr_cie_xyz* cie); + +extern LOCAL_SYM void +htrdr_cie_xyz_ref_put + (struct htrdr_cie_xyz* cie); + +/* Return a wavelength in nanometer */ +extern LOCAL_SYM double +htrdr_cie_xyz_sample_X + (struct htrdr_cie_xyz* cie, + const double r); /* Canonical number in [0, 1[ */ + +/* Return a wavelength in nanometer */ +extern LOCAL_SYM double +htrdr_cie_xyz_sample_Y + (struct htrdr_cie_xyz* cie, + const double r); /* Canonical number in [0, 1[ */ + +/* Return a wavelength in nanometer */ +extern LOCAL_SYM double +htrdr_cie_xyz_sample_Z + (struct htrdr_cie_xyz* cie, + const double r); /* Canonical number in [0, 1[ */ + +#endif /* HTRDR_cie_xyz_H */ + diff --git a/src/htrdr_compute_radiance_sw.c b/src/htrdr_compute_radiance_sw.c @@ -247,6 +247,7 @@ htrdr_compute_radiance_sw struct ssp_rng* rng, const double pos_in[3], const double dir_in[3], + const double wlen, /* In nanometer */ const size_t iband, const size_t iquad) { @@ -264,7 +265,6 @@ htrdr_compute_radiance_sw double dir_next[3]; double band_bounds[2]; /* In nanometers */ - double wlen; double R; double r; /* Random number */ double wo[3]; /* -dir */ @@ -291,13 +291,11 @@ htrdr_compute_radiance_sw (htrdr->sky, iband, iquad); SSF(phase_hg_setup(phase_hg, g)); - /* Fetch sun properties. Arbitrarily use the wavelength at the center of the - * band to retrieve the sun radiance of the current band. Note that the sun - * spectral data are defined by bands that, actually are the same of the SW - * spectral bands defined in the default "ecrad_opt_prot.txt" file provided - * by the HTGOP project. */ + /* Fetch sun properties. Note that the sun spectral data are defined by bands + * that, actually are the same of the SW spectral bands defined in the + * default "ecrad_opt_prot.txt" file provided by the HTGOP project. */ htsky_get_spectral_band_bounds(htrdr->sky, iband, band_bounds); - wlen = (band_bounds[0] + band_bounds[1]) * 0.5; + ASSERT(band_bounds[0] <= wlen && wlen <= band_bounds[1]); sun_solid_angle = htrdr_sun_get_solid_angle(htrdr->sun); L_sun = htrdr_sun_get_radiance(htrdr->sun, wlen); diff --git a/src/htrdr_draw_radiance.c b/src/htrdr_draw_radiance.c @@ -20,6 +20,7 @@ #include "htrdr_c.h" #include "htrdr_buffer.h" #include "htrdr_camera.h" +#include "htrdr_cie_xyz.h" #include "htrdr_solve.h" #include <high_tune/htsky.h> @@ -541,8 +542,9 @@ draw_pixel_sw double ray_dir[3]; double weight; double r0, r1; - size_t iband; - size_t iquad; + double wlen; /* Sampled wavelength into the spectral band */ + size_t iband; /* Sampled spectral band */ + size_t iquad; /* Sampled quadrature point into the spectral band */ double usec; /* Begin the registration of the time spent to in the realisation */ @@ -561,24 +563,18 @@ draw_pixel_sw /* Sample a spectral band and a quadrature point */ switch(ichannel) { - case 0: - htsky_sample_sw_spectral_data_CIE_1931_X - (htrdr->sky, r0, r1, &iband, &iquad); - break; - case 1: - htsky_sample_sw_spectral_data_CIE_1931_Y - (htrdr->sky, r0, r1, &iband, &iquad); - break; - case 2: - htsky_sample_sw_spectral_data_CIE_1931_Z - (htrdr->sky, r0, r1, &iband, &iquad); - break; + case 0: wlen = htrdr_cie_xyz_sample_X(htrdr->cie, r0); break; + case 1: wlen = htrdr_cie_xyz_sample_Y(htrdr->cie, r0); break; + case 2: wlen = htrdr_cie_xyz_sample_Z(htrdr->cie, r0); break; default: FATAL("Unreachable code.\n"); break; } + iband = htsky_find_spectral_band(htrdr->sky, wlen); + iquad = htsky_spectral_band_sample_quadrature(htrdr->sky, r1, iband); + /* Compute the luminance */ weight = htrdr_compute_radiance_sw - (htrdr, ithread, rng, ray_org, ray_dir, iband, iquad); + (htrdr, ithread, rng, ray_org, ray_dir, wlen, iband, iquad); ASSERT(weight >= 0); /* End the registration of the per realisation time */ diff --git a/src/htrdr_solve.h b/src/htrdr_solve.h @@ -63,6 +63,7 @@ htrdr_compute_radiance_sw struct ssp_rng* rng, const double pos[3], const double dir[3], + const double wlen, const size_t iband, /* Index of the spectral band */ const size_t iquad); /* Index of the quadrature point into the band */