commit afb9005008dc351bd03b750adfb5201ff1311307
parent 6282cc9c81992df7f49451731e438ca78a207e75
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Thu, 23 Mar 2017 14:43:31 +0100
Add an absorption parameter to the dielectric material
Handle the traversed medium in the solver and the path tracer
integrators.
Diffstat:
9 files changed, 79 insertions(+), 41 deletions(-)
diff --git a/src/ssol.h b/src/ssol.h
@@ -244,8 +244,9 @@ struct ssol_dielectric_shader {
ssol_shader_getter_T normal;
ssol_shader_getter_T eta_i; /* Refractive index of the current medium */
ssol_shader_getter_T eta_t; /* Refractive index of the opposite medium */
+ ssol_shader_getter_T absorption; /* Medium absorption */
};
-#define SSOL_DIELECTRIC_SHADER_NULL__ { NULL, NULL, NULL }
+#define SSOL_DIELECTRIC_SHADER_NULL__ { NULL, NULL, NULL, NULL }
static const struct ssol_dielectric_shader SSOL_DIELECTRIC_SHADER_NULL =
SSOL_DIELECTRIC_SHADER_NULL__;
diff --git a/src/ssol_atmosphere.c b/src/ssol_atmosphere.c
@@ -123,24 +123,13 @@ ssol_atmosphere_set_uniform_absorption
* Local functions
******************************************************************************/
double
-compute_atmosphere_transmissivity
+atmosphere_uniform_get_absorption
(const struct ssol_atmosphere* atmosphere,
- const double distance,
const double wavelength)
{
- double ka;
const struct ssol_spectrum* spectrum;
- if (!atmosphere)
- return 1;
-
- ASSERT(distance >= 0 && wavelength >= 0);
- switch (atmosphere->type) {
- case ATMOS_UNIFORM:
- spectrum = atmosphere->data.uniform.spectrum;
- ka = spectrum_interpolate(spectrum, wavelength);
- break;
- default: FATAL("Unreachable code\n"); break;
- }
- return exp(-ka * distance);
+ ASSERT(atmosphere && atmosphere->type == ATMOS_UNIFORM && wavelength >= 0);
+ spectrum = atmosphere->data.uniform.spectrum;
+ return spectrum_interpolate(spectrum, wavelength);
}
diff --git a/src/ssol_atmosphere_c.h b/src/ssol_atmosphere_c.h
@@ -42,9 +42,8 @@ struct ssol_atmosphere {
};
extern LOCAL_SYM double
-compute_atmosphere_transmissivity
+atmosphere_uniform_get_absorption
(const struct ssol_atmosphere* atmosphere,
- const double distance,
const double wavelength);
#endif /* SSOL_ATMOSPHERE_C_H */
diff --git a/src/ssol_draw_pt.c b/src/ssol_draw_pt.c
@@ -129,6 +129,7 @@ Li(struct ssol_scene* scn,
const float dir[3],
double val[3])
{
+ struct medium medium;
struct s3d_hit hit;
struct ray_data ray_data = RAY_DATA_NULL;
struct ssol_instance* inst;
@@ -157,10 +158,18 @@ Li(struct ssol_scene* scn,
f3_set(ray_org, org);
f3_set(ray_dir, dir);
+ /* Assume that the path starts from the air */
+ medium.eta = 1.00027;
+ medium.absorption = 0;
+
for(;;) {
S3D(scene_view_trace_ray
(view, ray_org, ray_dir, ray_range, &ray_data, &hit));
+ if(medium.absorption > 0) {
+ throughput *= exp(-medium.absorption * hit.distance);
+ }
+
if(S3D_HIT_NONE(&hit)) { /* Background lighting */
if(ray_dir[2] > 0) L += throughput * 1.e-1;
break;
@@ -195,7 +204,7 @@ Li(struct ssol_scene* scn,
surface_fragment_setup(&frag, o, wo, N, &hit.prim, hit.uv);
SSF(bsdf_clear(ctx->bsdf));
- res = material_shade_rendering(mtl, &frag, 1/*TODO wavelength*/, ctx->bsdf);
+ res = material_shade_rendering(mtl, &frag, 1/*TODO wavelength*/, &medium, ctx->bsdf);
CHECK(res, RES_OK);
/* Update the ray */
diff --git a/src/ssol_material.c b/src/ssol_material.c
@@ -38,6 +38,7 @@ dielectric_shade
(const struct ssol_material* mtl,
const struct surface_fragment* fragment,
const double wavelength, /* In nanometer */
+ struct medium* medium,
struct ssf_bsdf* bsdf)
{
struct ssf_bxdf* brdf = NULL;
@@ -47,8 +48,9 @@ dielectric_shade
double N[3];
double eta_i;
double eta_t;
+ double absorption;
res_T res = RES_OK;
- ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC);
+ ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC && medium);
ASSERT(bsdf);
shader = &mtl->data.dielectric;
@@ -60,8 +62,15 @@ dielectric_shade
FETCH(normal, N);
FETCH(eta_i, &eta_i);
FETCH(eta_t, &eta_t);
+ FETCH(absorption, &absorption);
#undef FETCH
+ if(!eq_eps(medium->eta, eta_i, 1.e-3)) {
+ log_error(mtl->dev, "Inconsistent medium definition.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
#define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0
/* Setup the reflective part */
CALL(ssf_fresnel_create
@@ -77,6 +86,9 @@ dielectric_shade
CALL(ssf_bsdf_add(bsdf, btdf, 0.5));
#undef CALL
+ medium->absorption = absorption;
+ medium->eta = eta_t;
+
exit:
if(brdf) SSF(bxdf_ref_put(brdf));
if(btdf) SSF(bxdf_ref_put(btdf));
@@ -257,6 +269,7 @@ shade
const struct surface_fragment* fragment,
const double wavelength, /* In nanometer */
const int rendering, /* Is material used for rendering */
+ struct medium* medium,
struct ssf_bsdf* bsdf)
{
res_T res = RES_OK;
@@ -265,7 +278,7 @@ shade
/* Specific material shading */
switch(mtl->type) {
case SSOL_MATERIAL_DIELECTRIC:
- res = dielectric_shade(mtl, fragment, wavelength, bsdf);
+ res = dielectric_shade(mtl, fragment, wavelength, medium, bsdf);
break;
case SSOL_MATERIAL_MATTE:
res = matte_shade(mtl, fragment, wavelength, bsdf);
@@ -288,7 +301,8 @@ check_shader_dielectric(const struct ssol_dielectric_shader* shader)
return shader
&& shader->normal
&& shader->eta_i
- && shader->eta_t;
+ && shader->eta_t
+ && shader->absorption;
}
static INLINE int
@@ -574,9 +588,10 @@ material_shade
(const struct ssol_material* mtl,
const struct surface_fragment* fragment,
const double wavelength, /* In nanometer */
+ struct medium* medium,
struct ssf_bsdf* bsdf)
{
- return shade(mtl, fragment, wavelength, 0, bsdf);
+ return shade(mtl, fragment, wavelength, 0, medium, bsdf);
}
res_T
@@ -584,8 +599,9 @@ material_shade_rendering
(const struct ssol_material* mtl,
const struct surface_fragment* fragment,
const double wavelength, /* In nanometer */
+ struct medium* medium,
struct ssf_bsdf* bsdf)
{
- return shade(mtl, fragment, wavelength, 1, bsdf);
+ return shade(mtl, fragment, wavelength, 1, medium, bsdf);
}
diff --git a/src/ssol_material_c.h b/src/ssol_material_c.h
@@ -23,6 +23,11 @@ struct s3d_primitive;
struct ssf_bsdf;
struct ssol_device;
+struct medium {
+ double eta; /* Refractive index of the medium */
+ double absorption;
+};
+
struct surface_fragment {
double dir[3]; /* World space incoming direction */
double pos[3]; /* World space position */
@@ -63,6 +68,7 @@ material_shade
(const struct ssol_material* mtl,
const struct surface_fragment* fragment,
const double wavelength, /* In nanometer */
+ struct medium* medium,
struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */
/* Material shading for rendering purposes */
@@ -71,6 +77,7 @@ material_shade_rendering
(const struct ssol_material* mtl,
const struct surface_fragment* fragment,
const double wavelength, /* In nanometer */
+ struct medium* medium,
struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */
#endif /* SSOL_MATERIAL_C_H */
diff --git a/src/ssol_solver.c b/src/ssol_solver.c
@@ -341,10 +341,15 @@ point_get_material(const struct point* pt)
static FINLINE res_T
point_shade
- (struct point* pt, struct ssf_bsdf* bsdf, struct ssp_rng* rng, double dir[3])
+ (struct point* pt,
+ struct ssf_bsdf* bsdf,
+ struct medium* medium,
+ struct ssp_rng* rng,
+ double dir[3])
{
struct surface_fragment frag;
- double wi[3], pdf, r;
+ double reflectivity = 1;
+ double wi[3], pdf;
res_T res;
/* TODO ensure that if `prim' was sampled, then the surface fragment setup
@@ -362,17 +367,17 @@ point_shade
/* Shade the surface fragment */
SSF(bsdf_clear(bsdf));
- res = material_shade(point_get_material(pt), &frag, pt->wl, bsdf);
+ res = material_shade(point_get_material(pt), &frag, pt->wl, medium, bsdf);
if(res != RES_OK) return res;
/* By convention, Star-SF assumes that incoming and reflected
* directions point outward the surface => negate incoming dir */
d3_minus(wi, pt->dir);
- r = ssf_bsdf_sample(bsdf, rng, wi, frag.Ns, dir, &pdf);
- ASSERT(0 <= r && r <= 1);
- pt->reflectivity_loss += (1 - r) * pt->weight;
- pt->weight *= r;
+ reflectivity = ssf_bsdf_sample(bsdf, rng, wi, frag.Ns, dir, &pdf);
+ ASSERT(0 <= reflectivity && reflectivity <= 1);
+ pt->reflectivity_loss += (1 - reflectivity) * pt->weight;
+ pt->weight *= reflectivity;
return RES_OK;
}
@@ -686,6 +691,7 @@ trace_radiative_path
const struct ssol_path_tracker* tracker, /* May be NULL */
FILE* output) /* May be NULL */
{
+ struct medium medium;
struct path path;
struct s3d_hit hit = S3D_HIT_NULL;
struct point pt;
@@ -702,6 +708,14 @@ trace_radiative_path
view_samp, view_rt, ran_sun_dir, ran_sun_wl, thread_ctx->rng, &is_lit);
if(res != RES_OK) goto error;
+ /* Assume that the path starts from an uniform atmosphere */
+ medium.eta = 1.0002772;
+ medium.absorption = 0;
+ if(scn->atmosphere) {
+ medium.absorption = atmosphere_uniform_get_absorption
+ (scn->atmosphere, pt.wl);
+ }
+
if(tracker) {
/* Add the first point of the starting segment */
if(tracker->sun_ray_length > 0) {
@@ -751,10 +765,9 @@ trace_radiative_path
range[0] = nextafterf(hit.distance, FLT_MAX);
range[1] = FLT_MAX;
} else {
-
/* Modulate the point weight wrt to its scattering functions and
* generate an outgoing direction */
- res = point_shade(&pt, thread_ctx->bsdf, thread_ctx->rng, pt.dir);
+ res = point_shade(&pt, thread_ctx->bsdf, &medium, thread_ctx->rng, pt.dir);
if(res != RES_OK) goto error;
/* Stop the radiative random walk */
@@ -790,10 +803,9 @@ trace_radiative_path
depth += mtl->type != SSOL_MATERIAL_VIRTUAL;
- /* Take into account the atmosphere attenuation along the new ray */
- if(scn->atmosphere) {
- const double transmissivity = compute_atmosphere_transmissivity
- (scn->atmosphere, hit.distance, pt.wl);
+ /* Take into account the medium attenuation */
+ if(medium.absorption > 0 && hit.distance > 0) {
+ const double transmissivity = exp(-medium.absorption * hit.distance);
ASSERT(0 < transmissivity && transmissivity <= 1);
pt.absorptivity_loss += (1 - transmissivity) * pt.weight;
pt.weight *= transmissivity;
diff --git a/src/test_ssol_material.c b/src/test_ssol_material.c
@@ -168,6 +168,7 @@ test_dielectric(struct ssol_device* dev)
dielectric.normal = get_shader_normal;
dielectric.eta_i = get_shader_refractive_index;
dielectric.eta_t = get_shader_refractive_index;
+ dielectric.absorption = get_shader_absorption;
CHECK(ssol_dielectric_set_shader(NULL, NULL), RES_BAD_ARG);
CHECK(ssol_dielectric_set_shader(material, NULL), RES_BAD_ARG);
@@ -175,17 +176,21 @@ test_dielectric(struct ssol_device* dev)
CHECK(ssol_dielectric_set_shader(material, &dielectric), RES_OK);
dielectric.normal = NULL;
- CHECK(ssol_dielectric_set_shader(material,&dielectric), RES_BAD_ARG);
+ CHECK(ssol_dielectric_set_shader(material, &dielectric), RES_BAD_ARG);
dielectric.normal = get_shader_normal;
dielectric.eta_i = NULL;
- CHECK(ssol_dielectric_set_shader(material,&dielectric), RES_BAD_ARG);
+ CHECK(ssol_dielectric_set_shader(material, &dielectric), RES_BAD_ARG);
dielectric.eta_i = get_shader_refractive_index;
dielectric.eta_t = NULL;
- CHECK(ssol_dielectric_set_shader(material,&dielectric), RES_BAD_ARG);
+ CHECK(ssol_dielectric_set_shader(material, &dielectric), RES_BAD_ARG);
dielectric.eta_t = get_shader_refractive_index;
+ dielectric.absorption = NULL;
+ CHECK(ssol_dielectric_set_shader(material, &dielectric), RES_BAD_ARG);
+ dielectric.absorption = get_shader_absorption;
+
CHECK(ssol_material_ref_put(material), RES_OK);
}
diff --git a/src/test_ssol_solver1.c b/src/test_ssol_solver1.c
@@ -269,7 +269,7 @@ main(int argc, char** argv)
CHECK(ssol_instance_set_receiver(target, SSOL_FRONT, 0), RES_OK);
CHECK(ssol_estimator_ref_put(estimator), RES_OK);
- /* Spectra mismatch */
+ /* Spectra mismatch */
desc.wavelengths = mismatch;
desc.wavelengths = ka;
desc.count = 2;