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:
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);