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 cfb45d5b8e83d37ffd27f14b1ab24ac6413e6d44
parent 408e9bc133b2de5581f1bc37a28414e84f69c8ed
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Sat, 10 Sep 2016 18:23:41 +0200

Update of the BxDF and BSDF API

Diffstat:
Msrc/ssf.h | 55+++++++++++++++++++++++++++++++++++++++++--------------
Msrc/ssf_bsdf.c | 45++++++++++++++++-----------------------------
Msrc/ssf_bxdf.c | 34++++++++++++++++++++++++----------
Msrc/ssf_specular_reflection.c | 30+++++++++++++++++++++++-------
Msrc/test_ssf_bsdf.c | 37+++++++++++--------------------------
Msrc/test_ssf_bxdf.c | 27++++-----------------------
Msrc/test_ssf_specular_reflection.c | 22+++++++++-------------
Msrc/test_ssf_utils.h | 16++++++++++++++++
8 files changed, 144 insertions(+), 122 deletions(-)

diff --git a/src/ssf.h b/src/ssf.h @@ -48,14 +48,28 @@ struct ssf_bxdf_type { res_T (*init)(struct mem_allocator* allocator, void* bxdf); void (*release)(void* bxdf); - double /* Sampled reflectivity */ + /* Sample an outgoing direction `wo' with respect to the ingoing direction + * `wi' and return the BxDF reflectivity along the sampled direction */ + double (*sample) (void* bxdf, const double u, /* Canonical random number */ const double v, /* Canonical random number */ - const double w[3], /* Normalized direction. Point inward the surface */ + const double wi[3], /* Normalized incoming dir. Point inward the surface */ const double N[3], /* Normalized normal */ - double dir[4]); /* Sampled direction. The PDF is stored in dir[3] */ + double wo[4]); /* Sampled direction. The PDF is stored in wo[3] */ + + double /* Reflectivity */ + (*eval) + (void* bxdf, + const double wi[3], /* Normalized incoming dir. Point inward the surface */ + const double wo[3]);/* Normalized outgoing dir. Point outward the surface */ + + double + (*pdf) + (void* bxdf, + const double wi[3], /* Normalized incoming dir. Point inward the surface */ + const double wo[3]);/* Normalized outgoing dir. Point outward the surface */ size_t sizeof_bxdf; /* In Bytes */ size_t alignof_bxdf; /* In Bytes */ @@ -79,6 +93,8 @@ BEGIN_DECLS /* Reflects the incoming direction with respect to the surface normal */ SSF_API const struct ssf_bxdf_type ssf_specular_reflection; +/* Glossy reflections with respect to a microfacet distribution */ +SSF_API const struct ssf_bxdf_type ssf_microfacet_reflection; /* Fresnel term for perfect reflection */ SSF_API const struct ssf_fresnel_type ssf_fresnel_no_op; @@ -107,21 +123,21 @@ ssf_bsdf_ref_put SSF_API res_T ssf_bsdf_add (struct ssf_bsdf* bsdf, - struct ssf_bxdf* bxdf); + struct ssf_bxdf* bxdf, + const double weight); SSF_API res_T ssf_bsdf_clear (struct ssf_bsdf* bsdf); -SSF_API res_T +SSF_API double ssf_bsdf_sample (struct ssf_bsdf* bsdf, const double u, /* Canonical number */ const double v, /* Canonical number */ - const double w[3], /* Normalized incoming direction */ + const double wi[3], /* Normalized incoming direction */ const double N[3], /* Normalized normal */ - double dir[4], /* Sampled direction. The PDF is stored in dir[3] */ - double* reflectivity); /* Sampled reflectivity */ + double wo[4]); /* Sampled direction. The PDF is stored in dir[3] */ /******************************************************************************* * Fresnel API - Define the equation of the fresnel term @@ -170,7 +186,7 @@ ssf_fresnel_dielectric_conductor_setup const double k_t); /* Imaginary part of the refraction id of the conductor */ /******************************************************************************* - * BxDF API - Bidirecitonal <Reflectance|Transmittance> distribution function. + * BxDF API - Bidirectional <Reflectance|Transmittance> distribution function. * Describes how the light is reflected|transmitted by a surface. ******************************************************************************/ SSF_API res_T @@ -187,18 +203,29 @@ SSF_API res_T ssf_bxdf_ref_put (struct ssf_bxdf* bxdf); -SSF_API res_T +SSF_API double /* Reflectivity */ ssf_bxdf_sample (struct ssf_bxdf* bxdf, const double u, /* Canonical random number */ const double v, /* Canonical random number */ - const double w[3], /* Normalized direction. Point inward the surface */ + const double wi[3], /* Normalized incoming dir. Point inward the surface */ const double N[3], /* Normalized normal */ - double dir[4], /* Sampled direction. The PDF is stored in dir[3] */ - double* reflectivity); /* Sampled reflectivity */ + double wo[4]); /* Sampled direction. The PDF is stored in dir[3] */ + +SSF_API double /* Reflectivity */ +ssf_bxdf_eval + (struct ssf_bxdf* bxdf, + const double wi[3], /* Normalized incoming dir. Point inward the surface */ + const double wo[3]);/* Normalized outgoing dir. Point outward the surface */ + +SSF_API double /* Probability */ +ssf_bxdf_pdf + (struct ssf_bxdf* bxdf, + const double wi[3], /* Normalized incoming dir. Point inward the surface */ + const double wo[3]);/* Normalized outgoing dir. Point outward the surface */ /* Retrieve the internal data of the BxDF. Usefull for user defined BxDFs on - * which the caller has to retrieve their data to setup the their parameters */ + * which the caller has to retrieve their data to setup their parameters */ SSF_API res_T ssf_bxdf_get_data (struct ssf_bxdf* bxdf, diff --git a/src/ssf_bsdf.c b/src/ssf_bsdf.c @@ -25,6 +25,7 @@ struct ssf_bsdf { struct ssf_bxdf* bxdfs[MAX_BxDFs]; + double weights[MAX_BxDFs]; size_t nbxdfs; ref_T ref; @@ -95,12 +96,13 @@ ssf_bsdf_ref_put(struct ssf_bsdf* bsdf) } res_T -ssf_bsdf_add(struct ssf_bsdf* bsdf, struct ssf_bxdf* bxdf) +ssf_bsdf_add(struct ssf_bsdf* bsdf, struct ssf_bxdf* bxdf, const double weight) { - if(!bsdf || !bxdf) return RES_BAD_ARG; + if(!bsdf || !bxdf || weight < 0) return RES_BAD_ARG; if(bsdf->nbxdfs >= MAX_BxDFs) return RES_MEM_ERR; SSF(bxdf_ref_get(bxdf)); bsdf->bxdfs[bsdf->nbxdfs] = bxdf; + bsdf->weights[bsdf->nbxdfs] = weight; ++bsdf->nbxdfs; return RES_OK; } @@ -119,16 +121,16 @@ ssf_bsdf_clear(struct ssf_bsdf* bsdf) } /* TODO fix the use of the u & v canonical random number. Currently u is used 2 - * times */ -res_T + * times + * TODO fix the sampling that is actually totally wrong */ +double ssf_bsdf_sample (struct ssf_bsdf* bsdf, const double u, const double v, - const double w[3], + const double wi[3], const double N[3], - double dir[4], - double* reflectivity) + double wo[4]) { const size_t PDF = 3; double reflectivities[MAX_BxDFs]; @@ -137,16 +139,9 @@ ssf_bsdf_sample double cumul[MAX_BxDFs]; double probas_sum; size_t i, n; - res_T res = RES_OK; - if(!bsdf || u<0 || u>=1 || v<0 || v>=1 || !w || !N || !dir || !reflectivity) { - res = RES_BAD_ARG; - goto error; - } - if(!d3_is_normalized(w) || !d3_is_normalized(N)) { - res = RES_BAD_ARG; - goto error; - } + ASSERT(bsdf && u>=0 && u<1 && v>=0 && v<1 && wi && N && wo); + ASSERT(d3_is_normalized(wi) && d3_is_normalized(N)); /* Build the probability distribution by sampling each BRDF */ n = 0; @@ -154,7 +149,7 @@ ssf_bsdf_sample FOR_EACH(i, 0, bsdf->nbxdfs) { struct ssf_bxdf* bxdf = bsdf->bxdfs[i]; - SSF(bxdf_sample(bxdf, u, v, w, N, dirs[n], reflectivities+n)); + reflectivities[n] = ssf_bxdf_sample(bxdf, u, v, wi, N, dirs[n]); if(reflectivities[n] <= 0 || dirs[n][PDF] <= 0) continue; /* Discard BxDF */ @@ -164,9 +159,8 @@ ssf_bsdf_sample } if(!n) { /* No valid BxDF to sample */ - d4_splat(dir, 0); - *reflectivity = 0; - goto exit; + d4_splat(wo, 0.0); + return 0.0; } /* Normalize the probability distribution */ @@ -179,14 +173,7 @@ ssf_bsdf_sample /* Finally sample the distribution */ FOR_EACH(i, 0, n-1) if(u <= cumul[i]) break; - d4_set(dir, dirs[i]); - *reflectivity = reflectivities[i]; - -exit: - return res; -error: - if(dir) d4_splat(dir, -DBL_MAX); - if(reflectivity) *reflectivity = -DBL_MAX; - goto exit; + d4_set(wo, dirs[i]); + return reflectivities[i]; } diff --git a/src/ssf_bxdf.c b/src/ssf_bxdf.c @@ -32,6 +32,8 @@ check_bxdf_type(const struct ssf_bxdf_type* type) && type->init && type->release && type->sample + && type->eval + && type->pdf && IS_POW2(type->alignof_bxdf); } @@ -111,22 +113,34 @@ ssf_bxdf_ref_put(struct ssf_bxdf* bxdf) return RES_OK; } -res_T +double ssf_bxdf_sample (struct ssf_bxdf* bxdf, const double u, const double v, - const double w[3], + const double wi[3], const double N[3], - double dir[4], - double* reflectivity) + double wo[4]) { - if(!bxdf || u<0 || u>=1 || v<0 || v>=1 || !w || !N || !dir || !reflectivity) - return RES_BAD_ARG; - if(!d3_is_normalized(w) || !d3_is_normalized(N)) - return RES_BAD_ARG; - *reflectivity = bxdf->type.sample(bxdf->data, u, v, w, N, dir); - return RES_OK; + ASSERT(bxdf && u>=0 && u<1 && v>=0 && v<1 && wi && N && wo); + ASSERT(d3_is_normalized(wi) && d3_is_normalized(N)); + return bxdf->type.sample(bxdf->data, u, v, wi, N, wo); +} + +double +ssf_bxdf_eval(struct ssf_bxdf* bxdf, const double wi[3], const double wo[3]) +{ + ASSERT(bxdf && wi && wo); + ASSERT(d3_is_normalized(wi) && d3_is_normalized(wo)); + return bxdf->type.eval(bxdf->data, wi, wo); +} + +double +ssf_bxdf_pdf(struct ssf_bxdf* bxdf, const double wi[3], const double wo[3]) +{ + ASSERT(bxdf && wi && wo); + ASSERT(d3_is_normalized(wi) && d3_is_normalized(wo)); + return bxdf->type.pdf(bxdf->data, wi, wo); } res_T diff --git a/src/ssf_specular_reflection.c b/src/ssf_specular_reflection.c @@ -43,23 +43,37 @@ specular_reflection_sample (void* data, const double u, const double v, - const double w[3], + const double wi[3], const double N[3], - double dir[4]) + double wo[4]) { struct specular_reflection* brdf = data; double cosi; - ASSERT(u >= 0 && u < 1 && v >= 0 && v < 1 && w && N && dir); + ASSERT(u >= 0 && u < 1 && v >= 0 && v < 1 && wi && N && wo); (void)u, (void)v; /* Reflection the incoming direction w[3] with respect to the normal */ - cosi = -d3_dot(w, N); - d3_muld(dir, N, 2*cosi); - d3_add(dir, dir, w); - dir[3] = 1; /* pdf */ + cosi = -d3_dot(wi, N); + d3_muld(wo, N, 2*cosi); + d3_add(wo, wo, wi); + wo[3] = 1.0; /* pdf */ return brdf->reflectivity; } +static double +specular_reflection_eval(void* data, const double wi[3], const double wo[3]) +{ + (void)data, (void)wi, (void)wo; + return 0.0; +} + +static double +specular_reflection_pdf(void* data, const double wi[3], const double wo[3]) +{ + (void)data, (void)wi, (void)wo; + return 0.0; +} + /******************************************************************************* * Exported symbols ******************************************************************************/ @@ -67,6 +81,8 @@ const struct ssf_bxdf_type ssf_specular_reflection = { specular_reflection_init, specular_reflection_release, specular_reflection_sample, + specular_reflection_eval, + specular_reflection_pdf, sizeof(struct specular_reflection), ALIGNOF(struct specular_reflection) }; diff --git a/src/test_ssf_bsdf.c b/src/test_ssf_bsdf.c @@ -27,7 +27,6 @@ main(int argc, char** argv) double w[3]; double N[3]; double dir[4]; - double refl; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -46,37 +45,23 @@ main(int argc, char** argv) CHECK(ssf_bxdf_create(&allocator, &ssf_specular_reflection, &bxdf), RES_OK); CHECK(ssf_specular_reflection_setup(bxdf, 2.0), RES_OK); - CHECK(ssf_bsdf_add(NULL, NULL), RES_BAD_ARG); - CHECK(ssf_bsdf_add(bsdf, NULL), RES_BAD_ARG); - CHECK(ssf_bsdf_add(NULL, bxdf), RES_BAD_ARG); - CHECK(ssf_bsdf_add(bsdf, bxdf), RES_OK); + CHECK(ssf_bsdf_add(NULL, NULL, -1.0), RES_BAD_ARG); + CHECK(ssf_bsdf_add(bsdf, NULL, -1.0), RES_BAD_ARG); + CHECK(ssf_bsdf_add(NULL, bxdf, -1.0), RES_BAD_ARG); + CHECK(ssf_bsdf_add(bsdf, bxdf, -1.0), RES_BAD_ARG); + CHECK(ssf_bsdf_add(NULL, NULL, 1.0), RES_BAD_ARG); + CHECK(ssf_bsdf_add(bsdf, NULL, 1.0), RES_BAD_ARG); + CHECK(ssf_bsdf_add(NULL, bxdf, 1.0), RES_BAD_ARG); + CHECK(ssf_bsdf_add(bsdf, bxdf, 1.0), RES_OK); d3_normalize(w, d3(w, -1, -1, 0)); d3(N, 0.0, 1.0, 0.0); - CHECK(ssf_bsdf_sample(NULL, 0, 0, w, N, dir, &refl), RES_BAD_ARG); - CHECK(ssf_bsdf_sample(bsdf, 0, -1, w, N, dir, &refl), RES_BAD_ARG); - CHECK(ssf_bsdf_sample(bsdf, 1, 0, w, N, dir, &refl), RES_BAD_ARG); - CHECK(ssf_bsdf_sample(bsdf, 0, 0, NULL, N, dir, &refl), RES_BAD_ARG); - CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, NULL, dir, &refl), RES_BAD_ARG); - CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, N, NULL, &refl), RES_BAD_ARG); - CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, N, dir, NULL), RES_BAD_ARG); - CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, N, dir, &refl), RES_OK); - CHECK(refl, 2.0); - CHECK(ssf_bsdf_sample(bsdf, 0.5, 0.5, w, N, dir, &refl), RES_OK); - CHECK(refl, 2.0); + CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, N, dir), 2.0); + CHECK(ssf_bsdf_sample(bsdf, 0.5, 0.5, w, N, dir), 2.0); CHECK(ssf_bsdf_clear(NULL), RES_BAD_ARG); CHECK(ssf_bsdf_clear(bsdf), RES_OK); - CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, N, dir, &refl), RES_OK); - CHECK(refl, 0.0); - - d3(w, -1, -1, 0); - CHECK(ssf_bsdf_sample(bsdf, 0.5, 0.5, w, N, dir, &refl), RES_BAD_ARG); - d3_normalize(w, w); - d3(N, 0.0, 2.0, 1.0); - CHECK(ssf_bsdf_sample(bsdf, 0.5, 0.5, w, N, dir, &refl), RES_BAD_ARG); - d3_normalize(N, N); - CHECK(ssf_bsdf_sample(bsdf, 0.5, 0.5, w, N, dir, &refl), RES_OK); + CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, N, dir), 0.0); CHECK(ssf_bsdf_ref_put(bsdf), RES_OK); CHECK(ssf_bxdf_ref_put(bxdf), RES_OK); diff --git a/src/test_ssf_bxdf.c b/src/test_ssf_bxdf.c @@ -78,7 +78,6 @@ main(int argc, char** argv) double w[3]; double N[3]; double dir[4]; - double rad; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -147,31 +146,13 @@ main(int argc, char** argv) data->v = 0.0; data->reflectivity = 1.234; - CHECK(ssf_bxdf_sample(NULL, 0, 0, w, N, dir, &rad), RES_BAD_ARG); - CHECK(ssf_bxdf_sample(bxdf, 0, -1, w, N, dir, &rad), RES_BAD_ARG); - CHECK(ssf_bxdf_sample(bxdf, 1, 0, w, N, dir, &rad), RES_BAD_ARG); - CHECK(ssf_bxdf_sample(bxdf, 0, 0, NULL, N, dir, &rad), RES_BAD_ARG); - CHECK(ssf_bxdf_sample(bxdf, 0, 0, w, NULL, dir, &rad), RES_BAD_ARG); - CHECK(ssf_bxdf_sample(bxdf, 0, 0, w, N, NULL, &rad), RES_BAD_ARG); - CHECK(ssf_bxdf_sample(bxdf, 0, 0, w, N, dir, NULL), RES_BAD_ARG); - CHECK(ssf_bxdf_sample(bxdf, 0, 0, w, N, dir, &rad), RES_OK); - CHECK(rad, 1.234); + CHECK(ssf_bxdf_sample(bxdf, 0, 0, w, N, dir), 1.234); data->u = 0.5; data->v = 0.5; - CHECK(ssf_bxdf_sample(bxdf, 0.5, 0.5, w, N, dir, &rad), RES_OK); - CHECK(rad, 1.234); - - d3(w, -1, -1, 0); - CHECK(ssf_bxdf_sample(bxdf, 0.5, 0.5, w, N, dir, &rad), RES_BAD_ARG); - d3_normalize(w, w); - d3(N, 0.0, 2.0, 1.0); - CHECK(ssf_bxdf_sample(bxdf, 0.5, 0.5, w, N, dir, &rad), RES_BAD_ARG); - d3_normalize(N, N); - d3_set(data->w, w); - d3_set(data->N, N); + CHECK(ssf_bxdf_sample(bxdf, 0.5, 0.5, w, N, dir), 1.234); + data->reflectivity = 3.14; - CHECK(ssf_bxdf_sample(bxdf, 0.5, 0.5, w, N, dir, &rad), RES_OK); - CHECK(rad, 3.14); + CHECK(ssf_bxdf_sample(bxdf, 0.5, 0.5, w, N, dir), 3.14); 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 @@ -27,7 +27,6 @@ main(int argc, char** argv) double w[3]; double N[3]; double dir[4]; - double refl; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -44,33 +43,30 @@ main(int argc, char** argv) d3(N, 0.0, 1.0, 0.0); d3_normalize(w, d3(w, -1.0, -1.0, 0.0)); - CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir, &refl), RES_OK); - CHECK(refl, 0.0); + CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir), 0.0); d3_normalize(w, d3(w, -1.0, -1.0, 0.0)); CHECK(ssf_specular_reflection_setup(brdf, 1.23), RES_OK); - CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir, &refl), RES_OK); - CHECK(refl, 1.23); + CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir), 1.23); CHECK(d3_eq_eps(d3(w, w[0], -w[1], 0.0), dir, 1.e-6), 1); + CHECK(ssf_bxdf_eval(brdf, w, dir), 0.0); + CHECK(ssf_bxdf_pdf(brdf, w, dir), 0.0); + d3(w, 0.0, -1.0, 0.0); - CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir, &refl), RES_OK); - CHECK(refl, 1.23); + CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir), 1.23); CHECK(d3_eq_eps(d3(w, 0.0, 1.0, 0.0), dir, 1.e-6), 1); d3_normalize(w, d3(w, -1.0, 1.0, 0.0)); - CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir, &refl), RES_OK); - CHECK(refl, 1.23); + CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir), 1.23); CHECK(d3_eq_eps(d3(w, w[0], -w[1], 0.0), dir, 1.e-6), 1); d3(w, 0.0, 1.0, 0.0); - CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir, &refl), RES_OK); - CHECK(refl, 1.23); + CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir), 1.23); CHECK(d3_eq_eps(d3(w, 0.0, -1.0, 0.0), dir, 1.e-6), 1); d3(w, -1.0, 0.0, 0.0); - CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir, &refl), RES_OK); - CHECK(refl, 1.23); + CHECK(ssf_bxdf_sample(brdf, 0, 0, w, N, dir), 1.23); CHECK(d3_eq_eps(w, dir, 1.e-6), 1); CHECK(ssf_bxdf_ref_put(brdf), RES_OK); diff --git a/src/test_ssf_utils.h b/src/test_ssf_utils.h @@ -48,10 +48,26 @@ bxdf_dummy_sample return 0.0; } +static double +bxdf_dummy_eval(void* bxdf, const double wi[3], const double wo[3]) +{ + (void)bxdf, (void)wi, (void)wo; + return 0.0; +} + +static double +bxdf_dummy_pdf(void* bxdf, const double wi[3], const double wo[3]) +{ + (void)bxdf, (void)wi, (void)wo; + return 0.0; +} + static const struct ssf_bxdf_type bxdf_dummy = { bxdf_dummy_init, bxdf_dummy_release, bxdf_dummy_sample, + bxdf_dummy_eval, + bxdf_dummy_pdf, 0, 1 };