commit b01aea4e75394c4a4ec7b7123a76a1b98ce50638
parent 74cdf4cf0cc81459ed3649201e146a54142d5e1f
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 22 Mar 2017 09:39:43 +0100
Add and test the specular transmission BTDF
Diffstat:
4 files changed, 228 insertions(+), 10 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -64,6 +64,7 @@ set(SSF_FILES_SRC
ssf_microfacet_distribution.c
ssf_microfacet_reflection.c
ssf_specular_reflection.c
+ ssf_specular_transmission.c
ssf_thin_specular_dielectric.c)
rcmake_prepend_path(SSF_FILES_SRC ${SSF_SOURCE_DIR})
rcmake_prepend_path(SSF_FILES_INC ${SSF_SOURCE_DIR})
@@ -112,6 +113,7 @@ if(NOT NO_TEST)
new_test(test_ssf_microfacet_distribution)
new_test(test_ssf_microfacet_reflection)
new_test(test_ssf_specular_reflection)
+ new_test(test_ssf_specular_transmission)
new_test(test_ssf_thin_specular_dielectric)
rcmake_copy_runtime_libraries(test_ssf_beckmann_distribution)
diff --git a/src/ssf.h b/src/ssf.h
@@ -192,18 +192,13 @@ SSF_API const struct ssf_bxdf_type ssf_microfacet2_reflection;
* ssf_fresnel_dielectric_dielectric is used to compute "rho", i.e. the
* reflected part during a single refraction event. But since real materials
* are not ideal dielectrics, the slab is also supposed to absorb incoming
- * radiation. The absorption coefficient "alpha" of the material can be either
- * provided by the user, or explicitly computed from "kappa", the imaginary
- * part of the material's refraction index, when available using:
- * alpha = 4*pi*kappa / lambda
- * lambda is the wavelength of the radiation inside the slab, computed as
- * lambda=lambda_in*n2/n1, with lambda_in the incoming wavelength, n1 and n2
- * being the real parts of the refraction indices respectively outside and
- * inside the material.
- *
- * TODO computing alpha from the kappa is actually not implemented */
+ * radiation. The absorption coefficient "alpha" of the material is provided
+ * by the user. */
SSF_API const struct ssf_bxdf_type ssf_thin_specular_dielectric;
+/* TODO comment me */
+SSF_API const struct ssf_bxdf_type ssf_specular_transmission;
+
/*******************************************************************************
* Built-in Fresnel terms
******************************************************************************/
@@ -379,6 +374,12 @@ ssf_thin_specular_dielectric_setup
const double eta_t, /* Refraction id of the thin dielectric slab */
const double thickness); /* Thickness of the slab */
+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 */
+ const double eta_t); /* Refraction id of the opposite medium */
+
/*******************************************************************************
* Fresnel API - Define the equation of the fresnel term
******************************************************************************/
diff --git a/src/ssf_specular_transmission.c b/src/ssf_specular_transmission.c
@@ -0,0 +1,118 @@
+/* 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>
+
+struct specular_transmission {
+ struct ssf_fresnel* fresnel;
+ double eta_i, eta_t;
+};
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static res_T
+specular_transmission_init
+ (struct mem_allocator* allocator, void* bxdf)
+{
+ struct specular_transmission* btdf = bxdf;
+ ASSERT(btdf);
+ return ssf_fresnel_create
+ (allocator, &ssf_fresnel_dielectric_dielectric, &btdf->fresnel);
+}
+
+static void
+specular_transmission_release(void* bxdf)
+{
+ struct specular_transmission* btdf = bxdf;
+ ASSERT(btdf);
+ if(btdf->fresnel) SSF(fresnel_ref_put(btdf->fresnel));
+}
+
+static double
+specular_transmission_sample
+ (void* bxdf,
+ struct ssp_rng* rng,
+ const double wo[3],
+ const double N[3],
+ double wi[3],
+ double* pdf)
+{
+ struct specular_transmission* btdf = bxdf;
+ double eta;
+ double cos_wi_N;
+ ASSERT(rng && wo && N && wi && pdf);
+ ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_dot(wo, N) > 0);
+ (void)rng;
+
+ SSF(fresnel_dielectric_dielectric_setup
+ (btdf->fresnel, btdf->eta_t, btdf->eta_i));
+
+ eta = btdf->eta_i / btdf->eta_t;
+ if(!refract(wi, wo, N, eta)) { /* Total internal reflection */
+ reflect(wi, wo, N); /* TODO ensure this */
+ return 0.0;
+ }
+ cos_wi_N = d3_dot(wi, N);
+ *pdf = INF;
+ return 1.0 - ssf_fresnel_eval(btdf->fresnel, fabs(cos_wi_N));
+}
+
+static double
+specular_transmission_eval
+ (void* data, const double wo[3], const double N[3], const double wi[3])
+{
+ (void)data, (void)wi, (void)N, (void)wo;
+ return 0.0;
+}
+
+static double
+specular_transmission_pdf
+ (void* data, const double wo[3], const double N[3], const double wi[3])
+{
+ (void)data, (void)wi, (void)N, (void)wo;
+ return 0.0;
+}
+
+/*******************************************************************************
+ * Exported symbols
+ ******************************************************************************/
+const struct ssf_bxdf_type ssf_specular_transmission = {
+ specular_transmission_init,
+ specular_transmission_release,
+ specular_transmission_sample,
+ specular_transmission_eval,
+ specular_transmission_pdf,
+ sizeof(struct specular_transmission),
+ ALIGNOF(struct specular_transmission)
+};
+
+res_T
+ssf_specular_transmission_setup
+ (struct ssf_bxdf* bxdf, const double eta_i, const double eta_t)
+{
+ struct specular_transmission* btdf;
+ if(!bxdf || eta_i <= 0 || eta_t <= 0) return RES_BAD_ARG;
+ if(!BXDF_TYPE_EQ(&bxdf->type, &ssf_specular_transmission)) return RES_BAD_ARG;
+ btdf = bxdf->data;
+ btdf->eta_i = eta_i;
+ btdf->eta_t = eta_t;
+ return RES_OK;
+}
+
diff --git a/src/test_ssf_specular_transmission.c b/src/test_ssf_specular_transmission.c
@@ -0,0 +1,97 @@
+/* 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_optics.h"
+#include "test_ssf_utils.h"
+
+#include <rsys/double3.h>
+
+int
+main(int argc, char** argv)
+{
+ const size_t NSTEPS = 100000;
+ struct mem_allocator allocator;
+ struct ssp_rng* rng;
+ struct ssf_bxdf* btdf;
+ struct ssf_bxdf* dummy;
+ struct ssf_fresnel* fresnel;
+ double wo[3], wi[3], tmp[3];
+ double N[3];
+ double pdf;
+ double R;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+ CHECK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK);
+ CHECK(ssf_bxdf_create(&allocator, &ssf_specular_transmission, &btdf), 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, 1.00027, 1.5), RES_OK);
+
+ CHECK(ssf_specular_transmission_setup(NULL, -1, -1), RES_BAD_ARG);
+ CHECK(ssf_specular_transmission_setup(btdf, -1, -1), RES_BAD_ARG);
+ CHECK(ssf_specular_transmission_setup(NULL, 1.00027, -1), RES_BAD_ARG);
+ CHECK(ssf_specular_transmission_setup(btdf, 1.00027, -1), RES_BAD_ARG);
+ CHECK(ssf_specular_transmission_setup(NULL, -1, 1.5), RES_BAD_ARG);
+ CHECK(ssf_specular_transmission_setup(btdf, -1, 1.5), RES_BAD_ARG);
+ CHECK(ssf_specular_transmission_setup(NULL, 1.00027, 1.5), RES_BAD_ARG);
+ CHECK(ssf_specular_transmission_setup(btdf, 1.00027, 1.5), RES_OK);
+ CHECK(ssf_specular_transmission_setup(dummy, 1.00027, 1.5), RES_BAD_ARG);
+
+ d3(N, 0.0, 1.0, 0.0);
+ d3_normalize(wo, d3(wo, 1.0, 1.0, 0.0));
+ R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &pdf);
+ CHECK(eq_eps(R, ssf_fresnel_eval(fresnel, fabs(d3_dot(N, wi))), 1.e-6), RES_OK);
+ CHECK(d3_eq_eps(wi, d3_minus(tmp, wo), 1.e-6), 0);
+ CHECK(wi[1] < 0, 1);
+ CHECK(IS_INF(pdf), 1);
+
+ CHECK(ssf_bxdf_eval(btdf, wo, N, wi), 0.0);
+ CHECK(ssf_bxdf_pdf(btdf, wo, N, wi), 0.0);
+
+ d3(wo, 0.0, 1.0, 0.0);
+ R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &pdf);
+ CHECK(eq_eps(R, ssf_fresnel_eval(fresnel, fabs(d3_dot(N, wi))), 1.e-6), RES_OK);
+ CHECK(IS_INF(pdf), 1);
+ CHECK(d3_eq_eps(wi, d3_minus(tmp, wo), 1.e-6), 1);
+
+ d3_normalize(wo, d3(wo, -1.0, 1.0, 0.0));
+ R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &pdf);
+ reflect(tmp, wi, N);
+ d3_normalize(wo, d3(wo, 1.0, 1.0, 0.0));
+ R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &pdf);
+ CHECK(d3_eq_eps(wi, tmp, 1.e-6), 1);
+
+ d3_normalize(wo, d3(wo, -1, 1.e-6, 0));
+ R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &pdf);
+ CHECK(eq_eps(R, 0, 1.e-3), 1);
+
+ /* Check total internal reflection */
+ CHECK(ssf_specular_transmission_setup(btdf, 1.5, 1.00027), RES_OK);
+ R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &pdf);
+ CHECK(eq_eps(R, 0, 1.e-3), 1);
+
+ CHECK(ssf_fresnel_ref_put(fresnel), RES_OK);
+ CHECK(ssf_bxdf_ref_put(btdf), RES_OK);
+ CHECK(ssf_bxdf_ref_put(dummy), RES_OK);
+ CHECK(ssp_rng_ref_put(rng), RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHECK(mem_allocated_size(), 0);
+ return 0;
+}