solstice-solver

Solver library of the solstice app
git clone git://git.meso-star.com/solstice-solver.git
Log | Files | Refs | README | LICENSE

commit 4439ddfbcf697412d44b2c8575f5eff959be5b3e
parent 4fbad2c17c7074c1ba25b5c0f41a3b65c6d9abd3
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 21 Apr 2017 11:24:55 +0200

Add support of spectral data to the [thin] dielectric material

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/ssol.h | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/ssol_c.h | 19+++++++++++++++++++
Asrc/ssol_data.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ssol_draw_pt.c | 12++++++------
Msrc/ssol_material.c | 61++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/ssol_material_c.h | 4++--
Msrc/ssol_ranst_sun_wl.c | 15++++++++-------
Msrc/ssol_solver.c | 11+++++++----
Msrc/ssol_spectrum.c | 13+++++++++++++
Msrc/ssol_spectrum_c.h | 6++++++
Msrc/ssol_sun.c | 3+--
Msrc/test_ssol_material.c | 32++++++++++++++++----------------
13 files changed, 286 insertions(+), 55 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -55,6 +55,7 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SSOL_FILES_SRC ssol_atmosphere.c ssol_camera.c + ssol_data.c ssol_device.c ssol_draw.c ssol_draw_pt.c diff --git a/src/ssol.h b/src/ssol.h @@ -118,6 +118,11 @@ enum ssol_attrib_usage { SSOL_ATTRIBS_COUNT__ }; +enum ssol_data_type { + SSOL_DATA_REAL, + SSOL_DATA_SPECTRUM +}; + /* Describe a vertex data */ struct ssol_vertex_data { enum ssol_attrib_usage usage; /* Semantic of the data */ @@ -232,11 +237,21 @@ struct ssol_punched_surface { static const struct ssol_punched_surface SSOL_PUNCHED_SURFACE_NULL = SSOL_PUNCHED_SURFACE_NULL__; +struct ssol_data { + enum ssol_data_type type; + union { + double real; + struct ssol_spectrum* spectrum; + } value; +}; +#define SSOL_DATA_NULL__ {SSOL_DATA_REAL, {0.0}} +static const struct ssol_data SSOL_DATA_NULL = SSOL_DATA_NULL__; + struct ssol_medium { - double absorptivity; - double refractive_index; + struct ssol_data absorptivity; + struct ssol_data refractive_index; }; -#define SSOL_MEDIUM_VACUUM__ { 0, 1 } +#define SSOL_MEDIUM_VACUUM__ {{SSOL_DATA_REAL, {0}}, {SSOL_DATA_REAL, {1}}} static const struct ssol_medium SSOL_MEDIUM_VACUUM = SSOL_MEDIUM_VACUUM__; struct ssol_surface_fragment { @@ -1164,6 +1179,57 @@ ssol_draw_pt ssol_write_pixels_T writer, void* writer_data); +/******************************************************************************* + * Data API + ******************************************************************************/ +SSOL_API struct ssol_data* +ssol_data_set_real + (struct ssol_data* data, + const double real); + +/* Get a reference onto the submitted spectrum */ +SSOL_API struct ssol_data* +ssol_data_set_spectrum + (struct ssol_data* data, + struct ssol_spectrum* spectrum); + +/* Release the reference on its associated spectrum, if defined */ +SSOL_API struct ssol_data* +ssol_data_clear + (struct ssol_data* data); + +SSOL_API struct ssol_data* +ssol_data_copy + (struct ssol_data* dst, + const struct ssol_data* src); + +SSOL_API int +ssol_data_eq + (const struct ssol_data* op0, + const struct ssol_data* op1); + + +/******************************************************************************* + * Medium API + ******************************************************************************/ +static FINLINE struct ssol_medium* +ssol_medium_clear(struct ssol_medium* medium) +{ + ASSERT(medium); + ssol_data_clear(&medium->absorptivity); + ssol_data_clear(&medium->refractive_index); + return medium; +} + +static FINLINE struct ssol_medium* +ssol_medium_copy(struct ssol_medium* dst, const struct ssol_medium* src) +{ + ASSERT(dst && src); + ssol_data_copy(&dst->absorptivity, &src->absorptivity); + ssol_data_copy(&dst->refractive_index, &src->refractive_index); + return dst; +} + END_DECLS #endif /* SSOL_H */ diff --git a/src/ssol_c.h b/src/ssol_c.h @@ -18,6 +18,7 @@ #include "ssol.h" #include "ssol_instance_c.h" +#include "ssol_spectrum_c.h" #include <rsys/math.h> #include <star/s3d.h> @@ -68,5 +69,23 @@ hit_filter_function void* realisation, void* filter_data); +static INLINE double +ssol_data_get_value(const struct ssol_data* data, const double wavelength) +{ + double val; + ASSERT(data && wavelength >= 0); + + switch(data->type) { + case SSOL_DATA_REAL: + val = data->value.real; + break; + case SSOL_DATA_SPECTRUM: + val = spectrum_interpolate(data->value.spectrum, wavelength); + break; + default: FATAL("Unreachable code\n"); break; + } + return val; +} + #endif /* SSOL_C_H */ diff --git a/src/ssol_data.c b/src/ssol_data.c @@ -0,0 +1,92 @@ +/* Copyright (C) CNRS 2016-2017 + * + * 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 SSOL_DATA_C_H +#define SSOL_DATA_C_H + +#include "ssol.h" + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +struct ssol_data* +ssol_data_set_real(struct ssol_data* data, const double real) +{ + const double r = real; + ASSERT(data); + ssol_data_clear(data); + data->type = SSOL_DATA_REAL; + data->value.real = r; + return data; +} + +struct ssol_data* +ssol_data_set_spectrum(struct ssol_data* data, struct ssol_spectrum* spectrum) +{ + ASSERT(data && spectrum); + if(data->type == SSOL_DATA_SPECTRUM && data->value.spectrum == spectrum) + return data; + ssol_data_clear(data); + data->type = SSOL_DATA_SPECTRUM; + data->value.spectrum = spectrum; + SSOL(spectrum_ref_get(spectrum)); + return data; +} + +struct ssol_data* +ssol_data_clear(struct ssol_data* data) +{ + ASSERT(data); + if(data->type != SSOL_DATA_SPECTRUM) return data; + ASSERT(data->value.spectrum); + SSOL(spectrum_ref_put(data->value.spectrum)); + *data = SSOL_DATA_NULL; + return data; +} + +struct ssol_data* +ssol_data_copy(struct ssol_data* dst, const struct ssol_data* src) +{ + ASSERT(dst && src); + if(dst == src) return dst; + dst->type = src->type; + switch(dst->type) { + case SSOL_DATA_REAL: + dst->value.real = src->value.real; + break; + case SSOL_DATA_SPECTRUM: + dst->value.spectrum = src->value.spectrum; + SSOL(spectrum_ref_get(dst->value.spectrum)); + break; + default: FATAL("Unreachable code.\n"); break; + } + return dst; +} + +int +ssol_data_eq(const struct ssol_data* a, const struct ssol_data* b) +{ + ASSERT(a && b); + if(a->type != b->type) return 0; + switch(a->type) { + case SSOL_DATA_REAL: return a->value.real == b->value.real; + case SSOL_DATA_SPECTRUM: return a->value.spectrum == b->value.spectrum; + default: FATAL("Unreachable code.\n"); break; + } + return 0; +} + +#endif /* SSOL_DATA_C_H */ + diff --git a/src/ssol_draw_pt.c b/src/ssol_draw_pt.c @@ -128,7 +128,7 @@ Li(struct ssol_scene* scn, const float dir[3], double val[3]) { - struct ssol_medium medium; + struct ssol_medium medium = SSOL_MEDIUM_VACUUM; struct s3d_hit hit; struct ray_data ray_data = RAY_DATA_NULL; struct ssol_instance* inst; @@ -159,15 +159,14 @@ Li(struct ssol_scene* scn, f3_set(ray_org, org); f3_set(ray_dir, dir); - /* Assume that the path starts from vacuum */ - medium = SSOL_MEDIUM_VACUUM; - for(;;) { + double absorptivity; S3D(scene_view_trace_ray (view, ray_org, ray_dir, ray_range, &ray_data, &hit)); - if(medium.absorptivity > 0) { - throughput *= exp(-medium.absorptivity * hit.distance); + absorptivity = ssol_data_get_value(&medium.absorptivity, 1/*Wavelength*/); + if(absorptivity > 0) { + throughput *= exp(-absorptivity * hit.distance); } if(S3D_HIT_NONE(&hit)) { /* Background lighting */ @@ -261,6 +260,7 @@ Li(struct ssol_scene* scn, d3_splat(val, L); exit: + ssol_medium_clear(&medium); return res; error: d3(val, 1, 1, 0); diff --git a/src/ssol_material.c b/src/ssol_material.c @@ -71,8 +71,8 @@ setup_dielectric_bsdf goto error; } - eta_i = mtl->out_medium.refractive_index; - eta_t = mtl->in_medium.refractive_index; + eta_i = ssol_data_get_value(&mtl->out_medium.refractive_index, wavelength); + eta_t = ssol_data_get_value(&mtl->in_medium.refractive_index, wavelength); #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 /* Setup the reflective part */ @@ -219,9 +219,11 @@ setup_thin_dielectric_bsdf ASSERT(bsdf); (void)wavelength, (void)fragment; - eta_i = mtl->out_medium.refractive_index; - eta_t = mtl->data.thin_dielectric.slab_medium.refractive_index; - absorptivity = mtl->data.thin_dielectric.slab_medium.absorptivity; + eta_i = ssol_data_get_value(&mtl->out_medium.refractive_index, wavelength); + eta_t = ssol_data_get_value + (&mtl->data.thin_dielectric.slab_medium.refractive_index, wavelength); + absorptivity = ssol_data_get_value + (&mtl->data.thin_dielectric.slab_medium.absorptivity, wavelength); thickness = mtl->data.thin_dielectric.thickness; /* Setup the BxDF */ @@ -275,9 +277,38 @@ check_shader_thin_differential(const struct ssol_thin_dielectric_shader* shader) static INLINE int check_medium(const struct ssol_medium* medium) { - return medium - && medium->refractive_index > 0 - && medium->absorptivity >= 0; + if(!medium) return 0; + + /* Check absorptivity in [0, INF) */ + switch(medium->absorptivity.type) { + case SSOL_DATA_REAL: + if(medium->absorptivity.value.real < 0) + return 0; + break; + case SSOL_DATA_SPECTRUM: + if(!medium->absorptivity.value.spectrum + || !spectrum_check_data(medium->absorptivity.value.spectrum, 0, DBL_MAX)) + return 0; + break; + default: FATAL("Unreachable code\n"); break; + } + + /* Check absorptivity in ]0, INF) */ + switch(medium->refractive_index.type) { + case SSOL_DATA_REAL: + if(medium->refractive_index.value.real <= 0) + return 0; + break; + case SSOL_DATA_SPECTRUM: + if(!medium->refractive_index.value.spectrum + || !spectrum_check_data + (medium->refractive_index.value.spectrum, DBL_EPSILON, DBL_MAX)) + return 0; + break; + default: FATAL("Unreachable code\n"); break; + } + + return 1; } static void @@ -420,8 +451,8 @@ ssol_dielectric_setup || !check_medium(inside_medium)) return RES_BAD_ARG; material->data.dielectric.dummy = 1; - material->out_medium = *outside_medium; - material->in_medium = *inside_medium; + ssol_medium_copy(&material->out_medium, outside_medium); + ssol_medium_copy(&material->in_medium, inside_medium); material->normal = shader->normal; return RES_OK; } @@ -470,8 +501,8 @@ ssol_thin_dielectric_setup return RES_BAD_ARG; material->data.thin_dielectric.slab_medium = *slab_medium; material->data.thin_dielectric.thickness = thickness; - material->out_medium = *outside_medium; - material->in_medium = *outside_medium; + ssol_medium_copy(&material->out_medium, outside_medium); + ssol_medium_copy(&material->in_medium, outside_medium); material->normal = shader->normal; return RES_OK; } @@ -647,16 +678,16 @@ material_get_next_medium /* The material is an interface between 2 media */ case SSOL_MATERIAL_DIELECTRIC: if(MEDIA_EQ(&mtl->out_medium, medium)) { - *next_medium = mtl->in_medium; + ssol_medium_copy(next_medium, &mtl->in_medium); } else { - *next_medium = mtl->out_medium; + ssol_medium_copy(next_medium, &mtl->out_medium); } break; /* The material is not an interface between 2 media */ case SSOL_MATERIAL_MATTE: case SSOL_MATERIAL_MIRROR: case SSOL_MATERIAL_THIN_DIELECTRIC: - *next_medium = *medium; + ssol_medium_copy(next_medium, medium); break; default: FATAL("Unreachable code\n"); break; } diff --git a/src/ssol_material_c.h b/src/ssol_material_c.h @@ -24,8 +24,8 @@ struct ssf_bsdf; struct ssol_device; #define MEDIA_EQ(A, B) \ - ( ((A)->refractive_index == (B)->refractive_index) \ - && ((A)->absorptivity == (B)->absorptivity)) + ( ssol_data_eq(&((A)->refractive_index), &((B)->refractive_index)) \ + && ssol_data_eq(&((A)->absorptivity), &((B)->absorptivity))) struct dielectric { int dummy; diff --git a/src/ssol_ranst_sun_wl.c b/src/ssol_ranst_sun_wl.c @@ -160,20 +160,21 @@ ranst_sun_wl_setup res_T res = RES_OK; if (sz && (!ran || !wavelengths || !intensities)) return RES_BAD_ARG; - if (sz > 1) { + if(sz <= 1) { + ran->type = WL_DIRAC; + ran->get = &ran_dirac_get; + ran->state.dirac.wavelength = sz ? wavelengths[0] : -1; + } else { ran->type = WL_PIECEWISE; ran->get = &ran_piecewise_get; res = ssp_ranst_piecewise_linear_create (ran->allocator, &ran->state.piecewise.spectrum); - if (res != RES_OK) goto error; + if(res != RES_OK) goto error; res = ssp_ranst_piecewise_linear_setup (ran->state.piecewise.spectrum, wavelengths, intensities, sz); - if (res != RES_OK) goto error; - } else { - ran->type = WL_DIRAC; - ran->get = &ran_dirac_get; - ran->state.dirac.wavelength = sz ? wavelengths[0] : -1; + if(res != RES_OK) goto error; } + exit: return res; error: diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -774,8 +774,8 @@ trace_radiative_path if(scn->atmosphere) { /* Assume that the path starts from an uniform atmosphere */ - medium.absorptivity = atmosphere_uniform_get_absorption - (scn->atmosphere, pt.wl); + ssol_data_set_real(&medium.absorptivity, + atmosphere_uniform_get_absorption(scn->atmosphere, pt.wl)); } if(tracker) { @@ -815,6 +815,7 @@ trace_radiative_path for(;;) { /* Here we go for the radiative random walk */ struct ray_data ray_data = RAY_DATA_NULL; struct ssol_material* mtl; + double absorptivity; /* Compute interaction with material */ mtl = point_get_material(&pt); @@ -874,8 +875,9 @@ trace_radiative_path depth += mtl->type != SSOL_MATERIAL_VIRTUAL; /* Take into account the medium attenuation */ - if(medium.absorptivity > 0 && hit.distance > 0) { - const double transmissivity = exp(-medium.absorptivity * hit.distance); + absorptivity = ssol_data_get_value(&medium.absorptivity, pt.wl); + if(absorptivity > 0 && hit.distance > 0) { + const double transmissivity = exp(-absorptivity * hit.distance); ASSERT(0 < transmissivity && transmissivity <= 1); pt.absorptivity_loss += (1 - transmissivity) * pt.weight; pt.weight *= transmissivity; @@ -905,6 +907,7 @@ trace_radiative_path } exit: if(tracker) path_release(&path); + ssol_medium_clear(&medium); return res; error: goto exit; diff --git a/src/ssol_spectrum.c b/src/ssol_spectrum.c @@ -111,6 +111,19 @@ spectrum_interpolate return intensity; } +int +spectrum_check_data + (const struct ssol_spectrum* spectrum, const double lower, const double upper) +{ + size_t i; + ASSERT(spectrum && lower <= upper); + FOR_EACH(i, 0, darray_double_size_get(&spectrum->intensities)) { + const double data = darray_double_cdata_get(&spectrum->intensities)[i]; + if(data < lower || data > upper) return 0; + } + return 1; +} + /******************************************************************************* * Exported ssol_spectrum functions ******************************************************************************/ diff --git a/src/ssol_spectrum_c.h b/src/ssol_spectrum_c.h @@ -40,4 +40,10 @@ spectrum_interpolate (const struct ssol_spectrum* spectrum, const double wavelength); +extern LOCAL_SYM int +spectrum_check_data + (const struct ssol_spectrum* spectrum, + const double lower, /* Inclusive lower bound */ + const double upper); /* Inclusive upper bound */ + #endif /* SSOL_SPECTRUM_C_H */ diff --git a/src/ssol_sun.c b/src/ssol_sun.c @@ -214,8 +214,7 @@ sun_create_distributions if(res != RES_OK) goto error; if(!sun->spectrum) { res = ranst_sun_wl_setup(ran_wl, NULL, NULL, 0); - } - else { + } else { res = ranst_sun_wl_setup(ran_wl, darray_double_cdata_get(&sun->spectrum->wavelengths), darray_double_cdata_get(&sun->spectrum->intensities), diff --git a/src/test_ssol_material.c b/src/test_ssol_material.c @@ -162,21 +162,21 @@ test_thin_dielectric(struct ssol_device* dev) CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); shader.normal = get_shader_normal; - mdm0.absorptivity = -1; + ssol_data_set_real(&mdm0.absorptivity, -1); CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); - mdm0.absorptivity = SSOL_MEDIUM_VACUUM.absorptivity; + ssol_data_copy(&mdm0.absorptivity, &SSOL_MEDIUM_VACUUM.absorptivity); - mdm0.refractive_index = 0; + ssol_data_set_real(&mdm0.refractive_index, 0); CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); - mdm0.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + ssol_data_copy(&mdm0.refractive_index, &SSOL_MEDIUM_VACUUM.refractive_index); - mdm1.absorptivity = -1; + ssol_data_set_real(&mdm1.absorptivity, -1); CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); - mdm1.absorptivity = SSOL_MEDIUM_VACUUM.absorptivity; + ssol_data_copy(&mdm1.absorptivity, &SSOL_MEDIUM_VACUUM.absorptivity); - mdm1.refractive_index = 0; + ssol_data_set_real(&mdm1.refractive_index, 0); CHECK(ssol_thin_dielectric_setup(mtl, &shader, &mdm0, &mdm1, 1), RES_BAD_ARG); - mdm1.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + ssol_data_copy(&mdm1.refractive_index, &SSOL_MEDIUM_VACUUM.refractive_index); CHECK(ssol_material_ref_put(mtl), RES_OK); } @@ -221,21 +221,21 @@ test_dielectric(struct ssol_device* dev) CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); dielectric.normal = get_shader_normal; - mdm0.refractive_index = 0; + ssol_data_set_real(&mdm0.refractive_index, 0); CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); - mdm0.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + ssol_data_copy(&mdm0.refractive_index, &SSOL_MEDIUM_VACUUM.refractive_index); - mdm1.refractive_index = 0; + ssol_data_set_real(&mdm1.refractive_index, 0); CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); - mdm1.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + ssol_data_copy(&mdm1.refractive_index, &SSOL_MEDIUM_VACUUM.refractive_index); - mdm0.absorptivity = -1; + ssol_data_set_real(&mdm0.absorptivity, -1); CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); - mdm0.absorptivity = SSOL_MEDIUM_VACUUM.refractive_index; + ssol_data_copy(&mdm0.absorptivity, &SSOL_MEDIUM_VACUUM.refractive_index); - mdm1.absorptivity = -1; + ssol_data_set_real(&mdm1.absorptivity, -1); CHECK(ssol_dielectric_setup(NULL, &dielectric, &mdm0, &mdm1), RES_BAD_ARG); - mdm1.refractive_index = SSOL_MEDIUM_VACUUM.refractive_index; + ssol_data_copy(&mdm1.refractive_index, &SSOL_MEDIUM_VACUUM.refractive_index); CHECK(ssol_material_ref_put(material), RES_OK); }