star-sf

Set of surface and volume scattering functions
git clone git://git.meso-star.fr/star-sf.git
Log | Files | Refs | README | LICENSE

commit 9b919713346e6a2127253bbcb398e32c221b9d72
parent 30af9168b267eff27759f7f8ec7dde91e11f8a39
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 26 Sep 2016 10:41:51 +0200

Add Fresnel to the specular BRDF and add the normal to the rho functions

Diffstat:
Msrc/ssf.h | 12++++++++----
Msrc/ssf_bsdf.c | 9+++++----
Msrc/ssf_bxdf.c | 6+++---
Msrc/ssf_lambertian_reflection.c | 6+++---
Msrc/ssf_microfacet_reflection.c | 21++++++++++++++-------
Msrc/ssf_specular_reflection.c | 44+++++++++++++++++++++++++++++++++-----------
Msrc/test_ssf_bsdf.c | 12++++++++----
Msrc/test_ssf_bxdf.c | 5+++--
Msrc/test_ssf_specular_reflection.c | 49++++++++++++++++++++++++++++++++++++++-----------
Msrc/test_ssf_utils.h | 4++--
10 files changed, 117 insertions(+), 51 deletions(-)

diff --git a/src/ssf.h b/src/ssf.h @@ -83,7 +83,8 @@ struct ssf_bxdf_type { double (*rho) (void* bxdf, - const double wo[3]); + const double wo[3], /* Normalized outgoing direction */ + const double N[3]); /* Normalized surface normal */ size_t sizeof_bxdf; /* In Bytes */ size_t alignof_bxdf; /* In Bytes */ @@ -220,7 +221,8 @@ ssf_bsdf_pdf SSF_API double /* \int_{2\pi} BSDF(wi, wo) |wi.N| dwi */ ssf_bsdf_rho (struct ssf_bsdf* bsdf, - const double wo[3]); + const double wo[3], + const double N[3]); /******************************************************************************* * BxDF API - Bidirectional <Reflectance|Transmittance> distribution function. @@ -269,7 +271,8 @@ ssf_bxdf_pdf SSF_API double /* \int_{2\pi} BxDF(wi, wo) |wi.N| dwi */ ssf_bxdf_rho (struct ssf_bxdf* bxdf, - const double wo[3]); + const double wo[3], + const double N[3]); /* Retrieve the internal data of the BxDF. Usefull for user defined BxDFs on * which the caller has to retrieve their data to setup their parameters */ @@ -281,7 +284,8 @@ ssf_bxdf_get_data SSF_API res_T ssf_specular_reflection_setup (struct ssf_bxdf* bxdf, - const double reflectivity); + const double reflectivity, + struct ssf_fresnel* fresnel); SSF_API res_T ssf_lambertian_reflection_setup diff --git a/src/ssf_bsdf.c b/src/ssf_bsdf.c @@ -174,7 +174,7 @@ ssf_bsdf_sample * rho the overall rho of the BSDF */ rho = 0; FOR_EACH(i, 0, bsdf->nbxdfs) { - probas[i] = bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo); + probas[i] = bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo, N); rho += probas[i]; } cumul[0] = (probas[0] /= rho); @@ -226,7 +226,7 @@ ssf_bsdf_pdf ASSERT(bsdf); bsdf_normalize_weights(bsdf); FOR_EACH(i, 0, bsdf->nbxdfs) { - const double tmp = bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo); + const double tmp = bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo, N); pdf += ssf_bxdf_pdf(bsdf->bxdfs[i], wo, N, wi) * tmp; rho += tmp; } @@ -254,14 +254,15 @@ ssf_bsdf_eval double ssf_bsdf_rho (struct ssf_bsdf* bsdf, - const double wo[3]) + const double wo[3], + const double N[3]) { double rho = 0; size_t i; ASSERT(bsdf); bsdf_normalize_weights(bsdf); FOR_EACH(i, 0, bsdf->nbxdfs) { - rho += bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo); + rho += bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo, N); } return rho; } diff --git a/src/ssf_bxdf.c b/src/ssf_bxdf.c @@ -154,10 +154,10 @@ ssf_bxdf_pdf } double -ssf_bxdf_rho(struct ssf_bxdf* bxdf, const double wo[3]) +ssf_bxdf_rho(struct ssf_bxdf* bxdf, const double wo[3], const double N[3]) { - ASSERT(bxdf && wo && d3_is_normalized(wo)); - return bxdf->type.rho(bxdf->data, wo); + ASSERT(bxdf && wo && d3_is_normalized(wo) && d3_is_normalized(N)); + return bxdf->type.rho(bxdf->data, wo, N); } res_T diff --git a/src/ssf_lambertian_reflection.c b/src/ssf_lambertian_reflection.c @@ -94,10 +94,10 @@ lambertian_reflection_pdf } static double -lambertian_reflection_rho(void* data, const double wo[3]) +lambertian_reflection_rho(void* data, const double wo[3], const double N[3]) { - ASSERT(data && wo); - (void)wo; + ASSERT(data && wo && N); + (void)wo, (void)N; return ((struct lambertian_reflection*)data)->reflectivity; } diff --git a/src/ssf_microfacet_reflection.c b/src/ssf_microfacet_reflection.c @@ -125,10 +125,10 @@ microfacet_reflection_pdf } static FINLINE double -microfacet_reflection_rho(void* data, const double wo[3]) +microfacet_reflection_rho(void* data, const double wo[3], const double N[3]) { - ASSERT(data && wo); - (void)data, (void)wo; + ASSERT(data && wo && N); + (void)data, (void)wo, (void)N; return 0; /* TODO */ } @@ -159,12 +159,19 @@ ssf_microfacet_reflection_setup if(!BXDF_TYPE_EQ(&bxdf->type, &ssf_microfacet_reflection)) return RES_BAD_ARG; - SSF(fresnel_ref_get(fresnel)); - SSF(microfacet_distribution_ref_get(distrib)); microfacet = bxdf->data; microfacet->reflectivity = reflectivity; - microfacet->fresnel = fresnel; - microfacet->distrib = distrib; + + if(microfacet->fresnel != fresnel) { + if(microfacet->fresnel) SSF(fresnel_ref_put(fresnel)); + SSF(fresnel_ref_get(fresnel)); + microfacet->fresnel = fresnel; + } + if(microfacet->distrib != distrib) { + if(microfacet->distrib) SSF(microfacet_distribution_ref_put(distrib)); + SSF(microfacet_distribution_ref_get(distrib)); + microfacet->distrib = distrib; + } return RES_OK; } diff --git a/src/ssf_specular_reflection.c b/src/ssf_specular_reflection.c @@ -21,6 +21,7 @@ struct specular_reflection { double reflectivity; + struct ssf_fresnel* fresnel; }; /******************************************************************************* @@ -37,7 +38,11 @@ specular_reflection_init(struct mem_allocator* allocator, void* bxdf) static void specular_reflection_release(void* bxdf) -{ (void)bxdf; } +{ + struct specular_reflection* spec = bxdf; + ASSERT(spec); + if(spec->fresnel) SSF(fresnel_ref_put(spec->fresnel)); +} static double specular_reflection_sample @@ -51,6 +56,7 @@ specular_reflection_sample { struct specular_reflection* brdf = data; double cos_wo_N; + double cos_wi_N; ASSERT(u >= 0 && u < 1 && v >= 0 && v < 1 && wi && N && wo); ASSERT(d3_is_normalized(wo) && d3_is_normalized(N)); (void)u, (void)v; @@ -59,9 +65,11 @@ specular_reflection_sample reflect(wi, wo, N); *pdf = 1.0; /* pdf */ - cos_wo_N = d3_dot(wo, N); + cos_wi_N = cos_wo_N = d3_dot(wo, N); /* TODO check this */ - return cos_wo_N <= 0 ? 0 : brdf->reflectivity / cos_wo_N/*|wi.N|*/; + return cos_wo_N > 0 + ? ssf_fresnel_eval(brdf->fresnel, cos_wi_N) * brdf->reflectivity / cos_wi_N + : 0; } static double @@ -81,11 +89,11 @@ specular_reflection_pdf } static double -specular_reflection_rho(void* data, const double wo[3]) +specular_reflection_rho(void* data, const double wo[3], const double N[3]) { - ASSERT(data && wo); - (void)wo; - return ((struct specular_reflection*)data)->reflectivity; + struct specular_reflection* brdf = data; + ASSERT(data && wo && N && d3_is_normalized(wo) && d3_is_normalized(N)); + return ssf_fresnel_eval(brdf->fresnel, d3_dot(wo, N)) * brdf->reflectivity; } /******************************************************************************* @@ -103,11 +111,25 @@ const struct ssf_bxdf_type ssf_specular_reflection = { }; res_T -ssf_specular_reflection_setup(struct ssf_bxdf* bxdf, const double reflectivity) +ssf_specular_reflection_setup + (struct ssf_bxdf* bxdf, + const double reflectivity, + struct ssf_fresnel* fresnel) { - if(!bxdf || reflectivity < 0 || reflectivity > 1) return RES_BAD_ARG; - if(!BXDF_TYPE_EQ(&bxdf->type, &ssf_specular_reflection)) return RES_BAD_ARG; - ((struct specular_reflection*)bxdf->data)->reflectivity = reflectivity; + struct specular_reflection* spec; + if(!bxdf || reflectivity < 0 || reflectivity > 1 || !fresnel) + return RES_BAD_ARG; + if(!BXDF_TYPE_EQ(&bxdf->type, &ssf_specular_reflection)) + return RES_BAD_ARG; + + spec = bxdf->data; + spec->reflectivity = reflectivity; + + if(spec->fresnel != fresnel) { + if(spec->fresnel) SSF(fresnel_ref_put(spec->fresnel)); + SSF(fresnel_ref_get(fresnel)); + spec->fresnel = fresnel; + } return RES_OK; } diff --git a/src/test_ssf_bsdf.c b/src/test_ssf_bsdf.c @@ -26,6 +26,7 @@ main(int argc, char** argv) struct ssf_bsdf* bsdf; struct ssf_bxdf* bxdf; struct ssf_bxdf* bxdf2; + struct ssf_fresnel* fresnel; double wo[3], wi[3], pdf; double rho; double N[3]; @@ -39,6 +40,7 @@ main(int argc, char** argv) CHECK(ssf_bsdf_create(NULL, NULL), RES_BAD_ARG); CHECK(ssf_bsdf_create(&allocator, NULL), RES_BAD_ARG); CHECK(ssf_bsdf_create(NULL, &bsdf), RES_OK); + CHECK(ssf_fresnel_create(&allocator, &ssf_fresnel_no_op, &fresnel), RES_OK); CHECK(ssf_bsdf_ref_get(NULL), RES_BAD_ARG); CHECK(ssf_bsdf_ref_get(bsdf), RES_OK); @@ -49,8 +51,8 @@ main(int argc, char** argv) CHECK(ssf_bsdf_create(&allocator, &bsdf), RES_OK); CHECK(ssf_bxdf_create(&allocator, &ssf_specular_reflection, &bxdf), RES_OK); CHECK(ssf_bxdf_create(&allocator, &ssf_specular_reflection, &bxdf2), RES_OK); - CHECK(ssf_specular_reflection_setup(bxdf, 0.123), RES_OK); - CHECK(ssf_specular_reflection_setup(bxdf2, 0.6789), RES_OK); + CHECK(ssf_specular_reflection_setup(bxdf, 0.123, fresnel), RES_OK); + CHECK(ssf_specular_reflection_setup(bxdf2, 0.6789, fresnel), RES_OK); CHECK(ssf_bsdf_add(NULL, NULL, -1.0), RES_BAD_ARG); CHECK(ssf_bsdf_add(bsdf, NULL, -1.0), RES_BAD_ARG); @@ -68,7 +70,7 @@ main(int argc, char** argv) CHECK(ssf_bsdf_eval(bsdf, wo, N, wi), 0); CHECK(ssf_bsdf_pdf(bsdf, wo, N, wi), 0); - CHECK(ssf_specular_reflection_setup(bxdf, 0.1234), RES_OK); + CHECK(ssf_specular_reflection_setup(bxdf, 0.1234, fresnel), RES_OK); R = ssf_bsdf_sample(bsdf, 0.5, 0.5, wo, N, wi, &pdf); CHECK(eq_eps(R, 0.1234 / d3_dot(wi, N), 1.e-6), 1); @@ -91,7 +93,7 @@ main(int argc, char** argv) E = sum / (double)NSTEPS; V = sqr_sum / (double)NSTEPS - E*E; SE = sqrt(V / (double)NSTEPS); - rho = ssf_bsdf_rho(bsdf, wo); + rho = ssf_bsdf_rho(bsdf, wo, N); CHECK(eq_eps(rho, E, SE), 1); CHECK(ssf_bsdf_clear(NULL), RES_BAD_ARG); @@ -103,9 +105,11 @@ main(int argc, char** argv) CHECK(ssf_bsdf_ref_put(bsdf), RES_OK); CHECK(ssf_bxdf_ref_put(bxdf), RES_OK); CHECK(ssf_bxdf_ref_put(bxdf2), RES_OK); + CHECK(ssf_fresnel_ref_put(fresnel), RES_OK); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); CHECK(mem_allocated_size(), 0); return 0; } + diff --git a/src/test_ssf_bxdf.c b/src/test_ssf_bxdf.c @@ -108,11 +108,12 @@ bxdf_pdf } static double -bxdf_rho(void* bxdf, const double wo[3]) +bxdf_rho(void* bxdf, const double wo[3], const double N[3]) { struct bxdf* BxDF = bxdf; NCHECK(BxDF, NULL); CHECK(d3_eq(BxDF->wo, wo), 1); + CHECK(d3_eq(BxDF->N, N), 1); return BxDF->rho; } @@ -214,7 +215,7 @@ main(int argc, char** argv) CHECK(ssf_bxdf_pdf(bxdf, wo, N, wi), data->pdf); data->rho = 0.314; - CHECK(ssf_bxdf_rho(bxdf, wo), data->rho); + CHECK(ssf_bxdf_rho(bxdf, wo, N), data->rho); CHECK(bxdf_is_init, 1); CHECK(ssf_bxdf_ref_put(bxdf), RES_OK); diff --git a/src/test_ssf_specular_reflection.c b/src/test_ssf_specular_reflection.c @@ -21,26 +21,37 @@ int main(int argc, char** argv) { + const size_t NSTEPS = 100000; struct mem_allocator allocator; struct ssf_bxdf* brdf; struct ssf_bxdf* dummy; + struct ssf_fresnel* fresnel; double wo[3], wi[3]; double N[3]; double pdf; + double rho; double R; + size_t i; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); CHECK(ssf_bxdf_create(&allocator, &ssf_specular_reflection, &brdf), RES_OK); CHECK(ssf_bxdf_create(&allocator, &bxdf_dummy, &dummy), RES_OK); + CHECK(ssf_fresnel_create + (&allocator, &ssf_fresnel_dielectric_dielectric, &fresnel), RES_OK); + CHECK(ssf_fresnel_dielectric_dielectric_setup(fresnel, 0.7, 1), RES_OK); - CHECK(ssf_specular_reflection_setup(NULL, -1.0), RES_BAD_ARG); - CHECK(ssf_specular_reflection_setup(brdf, -1.0), RES_BAD_ARG); - CHECK(ssf_specular_reflection_setup(NULL, 1.0), RES_BAD_ARG); - CHECK(ssf_specular_reflection_setup(brdf, 1.0), RES_OK); - CHECK(ssf_specular_reflection_setup(brdf, 0.0), RES_OK); - CHECK(ssf_specular_reflection_setup(brdf, 1.1), RES_BAD_ARG); - CHECK(ssf_specular_reflection_setup(dummy, 0.0), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(NULL, -1.0, NULL), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(brdf, -1.0, NULL), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(NULL, 1.0, NULL), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(brdf, 1.0, NULL), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(NULL, -1.0, fresnel), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(brdf, -1.0, fresnel), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(NULL, 1.0, fresnel), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(brdf, 1.0, fresnel), RES_OK); + CHECK(ssf_specular_reflection_setup(brdf, 0.0, fresnel), RES_OK); + CHECK(ssf_specular_reflection_setup(brdf, 1.1, fresnel), RES_BAD_ARG); + CHECK(ssf_specular_reflection_setup(dummy, 0.0, fresnel), RES_BAD_ARG); d3(N, 0.0, 1.0, 0.0); d3_normalize(wo, d3(wo, 1.0, 1.0, 0.0)); @@ -49,9 +60,10 @@ main(int argc, char** argv) CHECK(pdf, 1); d3_normalize(wo, d3(wo, 1.0, 1.0, 0.0)); - CHECK(ssf_specular_reflection_setup(brdf, 0.123), RES_OK); + CHECK(ssf_specular_reflection_setup(brdf, 0.123, fresnel), RES_OK); R = ssf_bxdf_sample(brdf, 0, 0, wo, N, wi, &pdf); - CHECK(eq_eps(R, 0.123 / d3_dot(N, wi), 1.e-6), 1); + CHECK(eq_eps + (R, ssf_fresnel_eval(fresnel, d3_dot(N, wi))*0.123/d3_dot(N, wi), 1.e-6), 1); CHECK(pdf, 1); CHECK(d3_eq_eps(d3(wo, -wo[0], wo[1], 0.0), wi, 1.e-6), 1); @@ -60,13 +72,15 @@ main(int argc, char** argv) d3(wo, 0.0, 1.0, 0.0); R = ssf_bxdf_sample(brdf, 0, 0, wo, N, wi, &pdf); - CHECK(eq_eps(R, 0.123, 1.e-6), 1); + CHECK(eq_eps + (R, ssf_fresnel_eval(fresnel, d3_dot(N, wi))*0.123, 1.e-6), 1); CHECK(pdf, 1); CHECK(d3_eq_eps(d3(wo, 0.0, 1.0, 0.0), wi, 1.e-6), 1); d3_normalize(wo, d3(wo, -1.0, 1.0, 0.0)); R = ssf_bxdf_sample(brdf, 0, 0, wo, N, wi, &pdf); - CHECK(eq_eps(R, 0.123 / d3_dot(N, wi), 1.e-6), 1); + CHECK(eq_eps + (R, ssf_fresnel_eval(fresnel, d3_dot(N, wi))*0.123/d3_dot(N, wi), 1.e-6), 1); CHECK(pdf, 1); CHECK(d3_eq_eps(d3(wo, -wo[0], wo[1], 0.0), wi, 1.e-6), 1); @@ -81,8 +95,21 @@ main(int argc, char** argv) CHECK(eq_eps(R, 0, 1.e-6), 1); CHECK(d3_eq_eps(d3(wo, -1.0, 0, 0), wi, 1.e-6), 1); + d3(wo, rand_canonic(), rand_canonic(), rand_canonic()); + d3_normalize(wo, wo); + R = ssf_bxdf_sample(brdf, rand_canonic(), rand_canonic(), wo, N, wi, &pdf); + rho = R * d3_dot(wi, N); + + FOR_EACH(i, 0, NSTEPS) { + const double u = rand_canonic(); + const double v = rand_canonic(); + CHECK(eq_eps(ssf_bxdf_sample(brdf, u, v, wo, N, wi, &pdf), R, 1.e-6), 1); + } + CHECK(eq_eps(rho, ssf_bxdf_rho(brdf, wo, N), 1.e-6), 1); + CHECK(ssf_bxdf_ref_put(brdf), RES_OK); CHECK(ssf_bxdf_ref_put(dummy), RES_OK); + CHECK(ssf_fresnel_ref_put(fresnel), RES_OK); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_ssf_utils.h b/src/test_ssf_utils.h @@ -72,9 +72,9 @@ bxdf_dummy_pdf } static double -bxdf_dummy_rho(void* bxdf, const double wo[3]) +bxdf_dummy_rho(void* bxdf, const double wo[3], const double N[3]) { - (void)bxdf, (void)wo; + (void)bxdf, (void)wo, (void)N; return 0.0; }