solstice-solver

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

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:
Msrc/ssol.h | 3++-
Msrc/ssol_atmosphere.c | 19++++---------------
Msrc/ssol_atmosphere_c.h | 3+--
Msrc/ssol_draw_pt.c | 11++++++++++-
Msrc/ssol_material.c | 26+++++++++++++++++++++-----
Msrc/ssol_material_c.h | 7+++++++
Msrc/ssol_solver.c | 38+++++++++++++++++++++++++-------------
Msrc/test_ssol_material.c | 11++++++++---
Msrc/test_ssol_solver1.c | 2+-
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;