solstice-solver

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

commit 13684364da22efe9ae2b247e148de79eb11b19fb
parent afb9005008dc351bd03b750adfb5201ff1311307
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 24 Mar 2017 14:51:03 +0100

Major refactoring of the dielectric material

The refractive index and the absorptivity are no more retrieved by the
shader but are constant for the whole material.

Diffstat:
Msrc/ssol.h | 13+++++++------
Msrc/ssol_draw_pt.c | 45+++++++++++++++++++++++++++++++++------------
Msrc/ssol_material.c | 95++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/ssol_material_c.h | 18++++++++++++++++--
Msrc/ssol_solver.c | 9+++++++--
Msrc/test_ssol_material.c | 88++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
6 files changed, 191 insertions(+), 77 deletions(-)

diff --git a/src/ssol.h b/src/ssol.h @@ -242,11 +242,8 @@ typedef void /* Dielectric material shader */ 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, NULL } +#define SSOL_DIELECTRIC_SHADER_NULL__ { NULL } static const struct ssol_dielectric_shader SSOL_DIELECTRIC_SHADER_NULL = SSOL_DIELECTRIC_SHADER_NULL__; @@ -709,9 +706,13 @@ ssol_material_set_param_buffer struct ssol_param_buffer* buf); SSOL_API res_T -ssol_dielectric_set_shader +ssol_dielectric_setup (struct ssol_material* mtl, - const struct ssol_dielectric_shader* shader); + const struct ssol_dielectric_shader* shader, + const double eta_i, /* Refractive index of the medium the ray comes from */ + const double eta_t, /* Refractive index of the opposite medium */ + const double absorptivity_i, /* Absorptivity of the medium the ray comes from */ + const double absorptivity_t); /* Absorptivity of the opposite medium */ SSOL_API res_T ssol_mirror_set_shader diff --git a/src/ssol_draw_pt.c b/src/ssol_draw_pt.c @@ -121,7 +121,7 @@ sun_lighting return 0; } -static void +static res_T Li(struct ssol_scene* scn, struct thread_context* ctx, struct s3d_scene_view* view, @@ -149,6 +149,7 @@ Li(struct ssol_scene* scn, float ray_dir[3]; enum ssol_side_flag side; int russian_roulette = 0; + int type; res_T res = RES_OK; ASSERT(scn && view && org && dir && val); @@ -158,9 +159,8 @@ 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; + /* Assume that the path starts from vacuum */ + medium = vacuum; for(;;) { S3D(scene_view_trace_ray @@ -204,8 +204,9 @@ 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*/, &medium, ctx->bsdf); - CHECK(res, RES_OK); + res = material_shade_rendering + (mtl, &frag, 1/*TODO wavelength*/, &medium, ctx->bsdf); + if(res != RES_OK) goto error; /* Update the ray */ ray_data.prim_from = hit.prim; @@ -220,9 +221,10 @@ Li(struct ssol_scene* scn, (scn->sun, view, &ray_data, ctx->bsdf, wo, N, ray_org); } - R = ssf_bsdf_sample(ctx->bsdf, ctx->rng, wo, frag.Ns, wi, &pdf); + R = ssf_bsdf_sample(ctx->bsdf, ctx->rng, wo, frag.Ns, wi, &type, &pdf); ASSERT(0 <= R && R <= 1); f3_set_d3(ray_dir, wi); + if(type & SSF_TRANSMISSION) material_get_next_medium(mtl, &medium, &medium); if(!russian_roulette) { throughput *= fabs(d3_dot(wi, N)) * R; @@ -238,6 +240,12 @@ Li(struct ssol_scene* scn, } } d3_splat(val, L); + +exit: + return res; +error: + d3(val, 1, 1, 0); + goto exit; } static void @@ -256,30 +264,43 @@ draw_pixel struct thread_context* ctx; double sum[3] = {0, 0, 0}; size_t isample; + res_T res = RES_OK; ASSERT(scn && cam && pix_coords && pix_sz && nsamples && pixel && data); ASSERT((size_t)ithread < darray_thread_context_size_get(thread_ctxs)); ctx = darray_thread_context_data_get(thread_ctxs) + ithread; FOR_EACH(isample, 0, nsamples) { + const int MAX_NFAILURES = 10; double weight[3]; float samp[2]; /* Pixel sample */ float ray_org[3], ray_dir[3]; + int nfailures = 0; /* Generate a sample into the pixel */ samp[0] = ((float)pix_coords[0]+ssp_rng_canonical_float(ctx->rng))*pix_sz[0]; samp[1] = ((float)pix_coords[1]+ssp_rng_canonical_float(ctx->rng))*pix_sz[1]; - /* Generate a ray starting from the pinhole camera and passing through the - * pixel sample */ - camera_ray(cam, samp, ray_org, ray_dir); + do { + /* Generate a ray starting from the pinhole camera and passing through the + * pixel sample */ + camera_ray(cam, samp, ray_org, ray_dir); + + /* Compute the radiance arriving through the sampled camera ray */ + res = Li(scn, ctx, view, ray_org, ray_dir, weight); + } while(res == RES_BAD_OP && ++nfailures < MAX_NFAILURES); + if(res != RES_OK) goto error; - /* Compute the radiance arriving through the sampled camera ray */ - Li(scn, ctx, view, ray_org, ray_dir, weight); d3_add(sum, sum, weight); } d3_divd(pixel, sum, (double)nsamples); +exit: + return; +error: + log_error(scn->dev, "Path tracing integrator error.\n"); + d3(pixel, 1, 1, 0); + goto exit; } /******************************************************************************* diff --git a/src/ssol_material.c b/src/ssol_material.c @@ -38,39 +38,34 @@ dielectric_shade (const struct ssol_material* mtl, const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ - struct medium* medium, + const struct medium* medium, struct ssf_bsdf* bsdf) { struct ssf_bxdf* brdf = NULL; struct ssf_bxdf* btdf = NULL; struct ssf_fresnel* fresnel = NULL; const struct ssol_dielectric_shader* shader; + double eta_i, eta_t; double N[3]; - double eta_i; - double eta_t; - double absorption; res_T res = RES_OK; - ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC && medium); - ASSERT(bsdf); + ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC); + ASSERT(medium && bsdf); shader = &mtl->data.dielectric; /* Fetch material attribs */ - #define FETCH(Attr, Dst) \ - shader->Attr(mtl->dev, mtl->buf, wavelength, fragment->pos, \ - fragment->Ng, fragment->Ns, fragment->uv, fragment->dir, Dst) - FETCH(normal, N); - FETCH(eta_i, &eta_i); - FETCH(eta_t, &eta_t); - FETCH(absorption, &absorption); - #undef FETCH + shader->normal(mtl->dev, mtl->buf, wavelength, fragment->pos, fragment->Ng, + fragment->Ns, fragment->uv, fragment->dir, N); - if(!eq_eps(medium->eta, eta_i, 1.e-3)) { - log_error(mtl->dev, "Inconsistent medium definition.\n"); - res = RES_BAD_ARG; + if(!MEDIA_EQ(medium, &mtl->out_medium)) { + log_error(mtl->dev, "Inconsistent medium description.\n"); + res = RES_BAD_OP; goto error; } + eta_i = mtl->out_medium.eta; + eta_t = mtl->in_medium.eta; + #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 /* Setup the reflective part */ CALL(ssf_fresnel_create @@ -86,9 +81,6 @@ 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)); @@ -269,7 +261,7 @@ shade const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ const int rendering, /* Is material used for rendering */ - struct medium* medium, + const struct medium* medium, struct ssf_bsdf* bsdf) { res_T res = RES_OK; @@ -278,7 +270,8 @@ shade /* Specific material shading */ switch(mtl->type) { case SSOL_MATERIAL_DIELECTRIC: - res = dielectric_shade(mtl, fragment, wavelength, medium, bsdf); + res = dielectric_shade + (mtl, fragment, wavelength, medium, bsdf); break; case SSOL_MATERIAL_MATTE: res = matte_shade(mtl, fragment, wavelength, bsdf); @@ -298,11 +291,7 @@ shade static INLINE int check_shader_dielectric(const struct ssol_dielectric_shader* shader) { - return shader - && shader->normal - && shader->eta_i - && shader->eta_t - && shader->absorption; + return shader && shader->normal; } static INLINE int @@ -373,6 +362,8 @@ ssol_material_create material->dev = dev; ref_init(&material->ref); material->type = type; + material->in_medium = vacuum; + material->out_medium = vacuum; exit: if (out_material) *out_material = material; @@ -456,14 +447,27 @@ ssol_material_create_thin_dielectric } res_T -ssol_dielectric_set_shader - (struct ssol_material* material, const struct ssol_dielectric_shader* shader) +ssol_dielectric_setup + (struct ssol_material* material, + const struct ssol_dielectric_shader* shader, + const double eta_i, + const double eta_t, + const double absorption_i, + const double absorption_t) { if(!material || material->type != SSOL_MATERIAL_DIELECTRIC - || !check_shader_dielectric(shader)) + || !check_shader_dielectric(shader) + || eta_i <= 0 + || eta_t <= 0 + || absorption_i < 0 + || absorption_t < 0) return RES_BAD_ARG; material->data.dielectric = *shader; + material->out_medium.eta = eta_i; + material->out_medium.absorption = absorption_i; + material->in_medium.eta = eta_t; + material->in_medium.absorption = absorption_t; return RES_OK; } @@ -588,7 +592,7 @@ material_shade (const struct ssol_material* mtl, const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ - struct medium* medium, + const struct medium* medium, struct ssf_bsdf* bsdf) { return shade(mtl, fragment, wavelength, 0, medium, bsdf); @@ -599,9 +603,36 @@ material_shade_rendering (const struct ssol_material* mtl, const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ - struct medium* medium, + const struct medium* medium, struct ssf_bsdf* bsdf) { return shade(mtl, fragment, wavelength, 1, medium, bsdf); } +res_T +material_get_next_medium + (const struct ssol_material* mtl, + const struct medium* medium, + struct medium* next_medium) +{ + ASSERT(mtl && medium && next_medium); + switch(mtl->type) { + /* The material is an interface between 2 media */ + case SSOL_MATERIAL_DIELECTRIC: + if(MEDIA_EQ(&mtl->out_medium, medium)) { + *next_medium = mtl->in_medium; + } else { + *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; + break; + default: FATAL("Unreachable code\n"); break; + } + return RES_OK; +} + diff --git a/src/ssol_material_c.h b/src/ssol_material_c.h @@ -28,6 +28,11 @@ struct medium { double absorption; }; +static const struct medium vacuum = { 1.0, 0.0 }; + +#define MEDIA_EQ(A, B) \ + (((A)->eta == (B)->eta) && ((A)->absorption == (B)->absorption)) + struct surface_fragment { double dir[3]; /* World space incoming direction */ double pos[3]; /* World space position */ @@ -49,6 +54,9 @@ struct ssol_material { struct ssol_thin_dielectric_shader thin_dielectric; } data; + struct medium out_medium; + struct medium in_medium; + struct ssol_param_buffer* buf; struct ssol_device* dev; ref_T ref; @@ -68,7 +76,7 @@ material_shade (const struct ssol_material* mtl, const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ - struct medium* medium, + const struct medium* medium, /* Current medium */ struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */ /* Material shading for rendering purposes */ @@ -77,7 +85,13 @@ material_shade_rendering (const struct ssol_material* mtl, const struct surface_fragment* fragment, const double wavelength, /* In nanometer */ - struct medium* medium, + const struct medium* medium, struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */ +extern LOCAL_SYM res_T +material_get_next_medium + (const struct ssol_material* mtl, + const struct medium* medium, /* Current mediu */ + struct medium* next_medium); + #endif /* SSOL_MATERIAL_C_H */ diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -347,10 +347,13 @@ point_shade struct ssp_rng* rng, double dir[3]) { + struct ssol_material* mtl; struct surface_fragment frag; double reflectivity = 1; double wi[3], pdf; + int type; res_T res; + ASSERT(pt && bsdf && medium && rng && dir); /* TODO ensure that if `prim' was sampled, then the surface fragment setup * remains valid in *all* situations, i.e. even though the point primitive @@ -366,19 +369,21 @@ point_shade surface_fragment_setup(&frag, pt->pos, pt->dir, pt->N, &pt->prim, pt->uv); /* Shade the surface fragment */ + mtl = point_get_material(pt); SSF(bsdf_clear(bsdf)); - res = material_shade(point_get_material(pt), &frag, pt->wl, medium, bsdf); + res = material_shade(mtl, &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); - reflectivity = ssf_bsdf_sample(bsdf, rng, wi, frag.Ns, dir, &pdf); + reflectivity = ssf_bsdf_sample(bsdf, rng, wi, frag.Ns, dir, &type, &pdf); ASSERT(0 <= reflectivity && reflectivity <= 1); pt->reflectivity_loss += (1 - reflectivity) * pt->weight; pt->weight *= reflectivity; + if(type & SSF_TRANSMISSION) material_get_next_medium(mtl, medium, medium); return RES_OK; } diff --git a/src/test_ssol_material.c b/src/test_ssol_material.c @@ -166,30 +166,72 @@ test_dielectric(struct ssol_device* dev) CHECK(type, SSOL_MATERIAL_DIELECTRIC); 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); - CHECK(ssol_dielectric_set_shader(NULL, &dielectric), RES_BAD_ARG); - CHECK(ssol_dielectric_set_shader(material, &dielectric), RES_OK); - - dielectric.normal = NULL; - 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); - dielectric.eta_i = get_shader_refractive_index; - - dielectric.eta_t = NULL; - 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_dielectric_setup(NULL, NULL, 0, 0, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 0, 0, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 0, 0, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 0, 0, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 1, 0, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 1, 0, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 1, 0, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 1, 0, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 0, 1, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 0, 1, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 0, 1, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 0, 1, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 1, 1, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 1, 1, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 1, 1, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 1, 1, -1, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 0, 0, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 0, 0, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 0, 0, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 0, 0, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 1, 0, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 1, 0, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 1, 0, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 1, 0, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 0, 1, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 0, 1, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 0, 1, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 0, 1, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 1, 1, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 1, 1, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 1, 1, 0, -1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 1, 1, 0, -1), RES_BAD_ARG); + + CHECK(ssol_dielectric_setup(NULL, NULL, 0, 0, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 0, 0, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 0, 0, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 0, 0, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 1, 0, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 1, 0, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 1, 0, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 1, 0, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 0, 1, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 0, 1, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 0, 1, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 0, 1, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 1, 1, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 1, 1, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 1, 1, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 1, 1, -1, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 0, 0, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 0, 0, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 0, 0, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 0, 0, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 1, 0, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 1, 0, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 1, 0, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 1, 0, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 0, 1, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 0, 1, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 0, 1, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 0, 1, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, NULL, 1, 1, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, NULL, 1, 1, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(NULL, &dielectric, 1, 1, 0, 1), RES_BAD_ARG); + CHECK(ssol_dielectric_setup(material, &dielectric, 1, 1, 0, 1), RES_OK); CHECK(ssol_material_ref_put(material), RES_OK); }