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:
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);
}