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 efcee87e6bce57328626cb732a109ecd165c4aa9
parent 56b93299b780bea43c907bfb94a1f4520f93ee36
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri,  8 Sep 2017 10:53:27 +0200

Merge branch 'release_0.2'

Diffstat:
MREADME.md | 11+++++++++++
Mcmake/CMakeLists.txt | 3++-
Msrc/ssf.h | 9+++++++++
Msrc/ssf_bsdf.c | 12++++--------
Msrc/ssf_optics.h | 2+-
Asrc/ssf_specular_dielectric_dielectric_interface.c | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ssf_thin_specular_dielectric.c | 12++++++++----
Msrc/test_ssf_thin_specular_dielectric.c | 37+++++++++++++++++++++++++++++++++++--
8 files changed, 216 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md @@ -24,6 +24,17 @@ package as well as all the aforementioned prerequisites. Finally generate the project from the `cmake/CMakeLists.txt` file by appending to the `CMAKE_PREFIX_PATH` variable the install directories of its dependencies. +## Release notes + +### Version 0.2 + +- Fix the thin-dielectric material to ensure the energy conservation property. +- Add the `ssf_specular_dielectric_dielectric_interface` BxDF. This scattering + function could be built by combining the `ssf_specular_reflection` and the + `ssf_specular_transmission` BxDFs into a BSDF but such combination does not + ensure the energy conservation property due to weaknesses into the BSDF + interface. + ## License Star-ScatteringFunctions is Copyright (C) |Meso|Star> 2016-2017 diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -39,7 +39,7 @@ rcmake_append_runtime_dirs(_runtime_dirs RSys StarSP) # Define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 1) +set(VERSION_MINOR 2) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) @@ -63,6 +63,7 @@ set(SSF_FILES_SRC ssf_lambertian_reflection.c ssf_microfacet_distribution.c ssf_microfacet_reflection.c + ssf_specular_dielectric_dielectric_interface.c ssf_specular_reflection.c ssf_specular_transmission.c ssf_thin_specular_dielectric.c) diff --git a/src/ssf.h b/src/ssf.h @@ -208,6 +208,9 @@ SSF_API const struct ssf_bxdf_type ssf_microfacet2_reflection; * by the user. */ SSF_API const struct ssf_bxdf_type ssf_thin_specular_dielectric; + +SSF_API const struct ssf_bxdf_type ssf_specular_dielectric_dielectric_interface; + /* Dirac distribution whose incoming direction `wi' is refracted wrt N and a * dielectric/dielectric fresnel term `Fr'. * fr(wo, wi) = (1 - Fr(|wi.N|)) * delta(wo - Refract(wi, N)) / |wi.N| @@ -397,6 +400,12 @@ ssf_thin_specular_dielectric_setup const double thickness); /* Thickness of the slab */ SSF_API res_T +ssf_specular_dielectric_dielectric_interface_setup + (struct ssf_bxdf* bxdf, + const double eta_i, + const double eta_t); + +SSF_API res_T ssf_specular_transmission_setup (struct ssf_bxdf* data, const double eta_i, /* Refraction id of the medium the ray travels in */ diff --git a/src/ssf_bsdf.c b/src/ssf_bsdf.c @@ -151,7 +151,7 @@ ssf_bsdf_sample int* type, double* out_pdf) { - double cumul[MAX_BxDFs]; + double cumul; double R; double r; double pdf; @@ -170,16 +170,12 @@ ssf_bsdf_sample bsdf_normalize_weights(bsdf); - /* Compute the cumulative from the normalized weights of the BxDFs */ - cumul[0] = bsdf->weights[0]; - FOR_EACH(i, 1, bsdf->nbxdfs) { - cumul[i] = bsdf->weights[i] + cumul[i-1]; - } - /* Sample a component */ r = ssp_rng_canonical(rng); + cumul = bsdf->weights[0]; FOR_EACH(i, 0, bsdf->nbxdfs-1) { - if(r <= cumul[i]) break; + if(r <= cumul) break; + cumul += bsdf->weights[i]; } /* Sample a direction from the selected component */ diff --git a/src/ssf_optics.h b/src/ssf_optics.h @@ -53,7 +53,7 @@ refract(double res[3], const double V[3], const double N[3], const double eta) cos_theta_i = d3_dot(V, N); sin2_theta_i = MMAX(0, 1.0 - cos_theta_i*cos_theta_i); sin2_theta_t = eta * eta * sin2_theta_i; - if(sin2_theta_t >= 1) return NULL; /* Total internal reflection */ + if(sin2_theta_t >= 1) return NULL; /* Total reflection */ cos_theta_t = sqrt(1 - sin2_theta_t); d3_muld(tmp0, V, eta); diff --git a/src/ssf_specular_dielectric_dielectric_interface.c b/src/ssf_specular_dielectric_dielectric_interface.c @@ -0,0 +1,146 @@ +/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "ssf.h" +#include "ssf_bxdf_c.h" +#include "ssf_optics.h" + +#include <rsys/double3.h> +#include <star/ssp.h> + +struct specular_dielectric_dielectric_interface { + struct ssf_fresnel* fresnel; + double eta_i; /* Refractive index of the incoming medium */ + double eta_t; /* Refractive index of the transmissive medium */ +}; + +/******************************************************************************* + * Private functions + ******************************************************************************/ +static res_T +ssf_specular_dielectric_dielectric_interface_init + (struct mem_allocator* allocator, void* bxdf) +{ + struct specular_dielectric_dielectric_interface* bsdf = bxdf; + ASSERT(bxdf); + return ssf_fresnel_create + (allocator, &ssf_fresnel_dielectric_dielectric, &bsdf->fresnel); +} + +static void +ssf_specular_dielectric_dielectric_interface_release(void* bxdf) +{ + struct specular_dielectric_dielectric_interface* bsdf = bxdf; + SSF(fresnel_ref_put(bsdf->fresnel)); +} + +static double +ssf_specular_dielectric_dielectric_interface_sample + (void* bxdf, + struct ssp_rng* rng, + const double wo[3], + const double N[3], + double wi[3], + int* type, + double* pdf) +{ + struct specular_dielectric_dielectric_interface* bsdf = bxdf; + struct ssf_fresnel* fresnel; + double wt[3]; + double *refracted; + double cos_wo_N; + double eta; /* Ratio of eta_i / eta_t */ + double R; + ASSERT(bxdf && rng && wi && N && wo && type && pdf); + ASSERT(d3_is_normalized(wo) && d3_is_normalized(N)); + ASSERT(d3_dot(wo, N) > -1.e-6); + (void)rng; + + eta = bsdf->eta_i / bsdf->eta_t; + refracted = refract(wt, wo, N, eta); + if(!refracted) { /* Total reflection */ + reflect(wi, wo, N); + *pdf = INF; + *type = SSF_SPECULAR | SSF_REFLECTION; + return 1; + } + fresnel = bsdf->fresnel; + + cos_wo_N = MMAX(0.0, d3_dot(wo, N)); + SSF(fresnel_dielectric_dielectric_setup(fresnel, bsdf->eta_i, bsdf->eta_t)); + R = ssf_fresnel_eval(fresnel, cos_wo_N); + + *pdf = INF; + + /* Sample the output direction wrt R */ + if(ssp_rng_canonical(rng) < R) { + reflect(wi, wo, N); + *type = SSF_SPECULAR | SSF_REFLECTION; + } else { + d3_set(wi, refracted); + *type = SSF_SPECULAR | SSF_TRANSMISSION; + } + return 1; +} + +static double +ssf_specular_dielectric_dielectric_interface_eval + (void* bxdf, const double wo[3], const double N[3], const double wi[3]) +{ + (void)bxdf, (void)wo, (void)N, (void)wi; + return 0.0; +} + +static double +ssf_specular_dielectric_dielectric_interface_pdf + (void* bxdf, const double wo[3], const double N[3], const double wi[3]) +{ + (void)bxdf, (void)wo, (void)N, (void)wi; + return 0.0; +} + +/******************************************************************************* + * Exported symbols + ******************************************************************************/ +const struct ssf_bxdf_type ssf_specular_dielectric_dielectric_interface = { + ssf_specular_dielectric_dielectric_interface_init, + ssf_specular_dielectric_dielectric_interface_release, + ssf_specular_dielectric_dielectric_interface_sample, + ssf_specular_dielectric_dielectric_interface_eval, + ssf_specular_dielectric_dielectric_interface_pdf, + sizeof(struct specular_dielectric_dielectric_interface), + ALIGNOF(struct specular_dielectric_dielectric_interface) +}; + +res_T +ssf_specular_dielectric_dielectric_interface_setup + (struct ssf_bxdf* bxdf, + const double eta_i, + const double eta_t) +{ + struct specular_dielectric_dielectric_interface* bsdf; + + if(!bxdf || eta_i <= 0 || eta_t <= 0) + return RES_BAD_ARG; + if(!BXDF_TYPE_EQ(&bxdf->type, &ssf_specular_dielectric_dielectric_interface)) + return RES_BAD_ARG; + + bsdf = bxdf->data; + + bsdf->eta_i = eta_i; + bsdf->eta_t = eta_t; + return RES_OK; +} + diff --git a/src/ssf_thin_specular_dielectric.c b/src/ssf_thin_specular_dielectric.c @@ -73,7 +73,7 @@ thin_specular_dielectric_sample (void)rng; eta = bsdf->eta_i / bsdf->eta_t; - if(!refract(wt, wo, N, eta)) { /* Total internal reflection */ + if(!refract(wt, wo, N, eta)) { /* Total reflection */ reflect(wi, wo, N); *pdf = INF; *type = SSF_SPECULAR | SSF_REFLECTION; @@ -107,14 +107,18 @@ thin_specular_dielectric_sample *pdf = INF; /* Importance sample the BTDF wrt R */ - if(ssp_rng_canonical(rng) < R) { /* Sample the reflective part */ + if(ssp_rng_uniform_double(rng, 0, R + T) < R) { reflect(wi, wo, N); *type = SSF_SPECULAR | SSF_REFLECTION; - return 1; } else { /* Sample the transmissive part */ d3_minus(wi, wo); *type = SSF_SPECULAR | SSF_TRANSMISSION; - return T; + } + if(bsdf->absorption == 0) { + /* avoid numerical loss of energy if no absorption */ + return 1; + } else { + return R + T; } } diff --git a/src/test_ssf_thin_specular_dielectric.c b/src/test_ssf_thin_specular_dielectric.c @@ -21,7 +21,7 @@ int main(int argc, char** argv) { - const size_t NSTEPS = 100000; + const size_t NSTEPS = 1000000; struct mem_allocator allocator; struct ssp_rng* rng; struct ssf_bxdf* bsdf; @@ -33,6 +33,8 @@ main(int argc, char** argv) double refract[3]; double pdf; double R; /* Directional reflectance */ + double SR, ST; + double SR2 = 0, ST2 = 0; size_t i; int type; (void)argc, (void)argv; @@ -109,14 +111,45 @@ main(int argc, char** argv) d3_normalize(wo, d3(wo, 1, 0.0000001, 0)); CHECK(ssf_thin_specular_dielectric_setup(bsdf, 0, 1.4, 1.0, 1), RES_OK); R = ssf_bxdf_sample(bsdf, rng, wo, N, wi, &type, &pdf); - NCHECK(R, 0); + CHECK(R, 1); CHECK(IS_INF(pdf), 1); CHECK(d3_eq_eps(wi, d3(tmp, -wo[0], wo[1], 0), 1.e-6), 1); CHECK(type, SSF_SPECULAR | SSF_REFLECTION); + FOR_EACH(i, 0, NSTEPS) { + R = ssf_bxdf_sample(bsdf, rng, wo, N, wi, &type, &pdf); + CHECK(type & SSF_REFLECTION, 1); + CHECK(R, 1); + } + + /* Check T VS R proportion and E conservation */ + SR = ST = 0; + SR2 = ST2 = 0; + d3(wo, 0.0, 1.0, 0.0); + FOR_EACH(i, 0, NSTEPS) { + R = ssf_bxdf_sample(bsdf, rng, wo, N, wi, &type, &pdf); + if (type & SSF_TRANSMISSION) { + ST += R; ST2 += R*R; + } else { + SR += R; SR2 += R*R; + } + } + + #define MEAN(x, n) ((x) / (double)(n)) + #define VAR(x, x2, n) (MEAN((x2), (n)) - MEAN((x), (n))*MEAN((x), (n))) + #define STD(x, x2, n) \ + (VAR((x), (x2), (n)) > 0 ? sqrt(VAR((x), (x2), (n)) / (double)(n)) : 0) + /* Check conservation of energy */ + CHECK(MEAN(SR+ST, NSTEPS), 1); + /* Check T VS R proportion */ + CHECK(eq_eps(MEAN(SR, NSTEPS), 0.0540540540, 3 * STD(SR,SR2,NSTEPS)), 1); + #undef MEAN + #undef VAR + #undef STD wo[0] = ssp_rng_uniform_double(rng, -1, 1); wo[1] = ssp_rng_uniform_double(rng, -1, 1); wo[2] = ssp_rng_uniform_double(rng, -1, 1); + if(d3_dot(wo, N) < 0) d3_minus(N, N); d3_normalize(wo, wo); d3_sub(reflect, d3_muld(reflect, N, 2*d3_dot(wo, N)), wo); d3_minus(refract, wo);