commit 25ac110242a03da64b83b8a0725e96bd870fe5d4
parent 8084172850338aa5a819ddfdce67f057c8414f18
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 1 Mar 2017 09:15:38 +0100
Implement a thin transparent dielectric built-in BxDF
This BxDF is actually a BSDF with a transmissive and a reflective part
that follow a purely specular model. It can be used to approximate thin
mediums whose thickness is so tiny that no geometry is required to
simulate it.
Diffstat:
4 files changed, 213 insertions(+), 2 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -64,7 +64,8 @@ set(SSF_FILES_SRC
ssf_lambertian_reflection.c
ssf_microfacet_distribution.c
ssf_microfacet_reflection.c
- ssf_specular_reflection.c)
+ ssf_specular_reflection.c
+ ssf_thin_transparent_dielectric.c)
rcmake_prepend_path(SSF_FILES_SRC ${SSF_SOURCE_DIR})
rcmake_prepend_path(SSF_FILES_INC ${SSF_SOURCE_DIR})
rcmake_prepend_path(SSF_FILES_INC_API ${SSF_SOURCE_DIR})
diff --git a/src/ssf.h b/src/ssf.h
@@ -181,6 +181,9 @@ SSF_API const struct ssf_bxdf_type ssf_microfacet_reflection;
* associated directional reflectance */
SSF_API const struct ssf_bxdf_type ssf_microfacet2_reflection;
+/* TODO comment me */
+SSF_API const struct ssf_bxdf_type ssf_thin_transparent_dielectric;
+
/*******************************************************************************
* Built-in Fresnel terms
******************************************************************************/
@@ -348,6 +351,16 @@ ssf_microfacet_reflection_setup
struct ssf_fresnel* fresnel,
struct ssf_microfacet_distribution* distrib);
+SSF_API res_T
+ssf_thin_transparent_dielectric_setup
+ (struct ssf_bxdf* data,
+ const double transmissivity,
+ /* Refraction id of the medium the incoming ray travels in */
+ const double eta_i,
+ /* Refraction id of the medium the outgoing transmission ray travels in */
+ const double eta_t,
+ const double thickness);
+
/*******************************************************************************
* Fresnel API - Define the equation of the fresnel term
******************************************************************************/
diff --git a/src/ssf_optics.h b/src/ssf_optics.h
@@ -18,7 +18,8 @@
#include <rsys/double3.h>
-/* Reflect the vector V wrt to the normal N */
+/* Reflect the vector V wrt the normal N. By convention V points outward the
+ * surface. */
static INLINE double*
reflect(double res[3], const double V[3], const double N[3])
{
@@ -32,5 +33,33 @@ reflect(double res[3], const double V[3], const double N[3])
return res;
}
+/* Refract the vect V wrt the normal N using the relative refractive index eta.
+ * Eta is the refraction index of the outside medium (where N points into)
+ * devided by the refraction index of the inside medium. By convention N and V
+ * points on the same side of the surface. Return res or NULL if the refraction
+ * is impossible. */
+static INLINE double*
+refract(double res[3], const double V[3], const double N[3], const double eta)
+{
+ double tmp0[3];
+ double tmp1[3];
+ double cos_theta_i;
+ double cos_theta_t;
+ double sin2_theta_i;
+ double sin2_theta_t;
+
+ ASSERT(res && V && N);
+ ASSERT(d3_is_normalized(V) && d3_is_normalized(N));
+ 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 */
+ cos_theta_t = sqrt(1 - sin2_theta_t);
+
+ d3_muld(tmp0, V, eta);
+ d3_muld(tmp1, N, eta * cos_theta_i - cos_theta_t);
+ return d3_sub(res, tmp1, tmp0);
+}
+
#endif /* SSF_OPTICS_H */
diff --git a/src/ssf_thin_transparent_dielectric.c b/src/ssf_thin_transparent_dielectric.c
@@ -0,0 +1,168 @@
+/* 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 thin_transparent_dielectric {
+ struct ssf_fresnel* fresnel;
+ double T; /* Transmissivity in [0, 1] */
+ double eta_i; /* Refractive index of the incoming medium */
+ double eta_t; /* Refractive index of the transmissive medium */
+ double thickness;
+};
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static res_T
+thin_transparent_dielectric_init
+ (struct mem_allocator* allocator, void* bxdf)
+{
+ struct thin_transparent_dielectric* bsdf = bxdf;
+ ASSERT(bxdf);
+ return ssf_fresnel_create
+ (allocator, &ssf_fresnel_dielectric_dielectric, &bsdf->fresnel);
+}
+
+static void
+thin_transparent_dielectric_release(void* bxdf)
+{
+ struct thin_transparent_dielectric* bsdf = bxdf;
+ SSF(fresnel_ref_put(bsdf->fresnel));
+}
+
+static double
+thin_transparent_dielectric_sample
+ (void* bxdf,
+ struct ssp_rng* rng,
+ const double wo[3],
+ const double N[3],
+ double wi[3],
+ double* pdf)
+{
+ struct thin_transparent_dielectric* bsdf = bxdf;
+ struct ssf_fresnel* fresnel;
+ double wt[3], tmp[3];
+ double cos_wo_N, cos_wt_N;
+ double dst;
+ double eta; /* Ratio of eta_i / eta_t */
+ double R, T;
+ double rho1, rho2, rho2_sqr;
+ double tau, tau_sqr;
+ ASSERT(bxdf && rng && wi && N && wo);
+ ASSERT(d3_is_normalized(wo) && d3_is_normalized(N));
+ (void)rng;
+
+ eta = bsdf->eta_i / bsdf->eta_t;
+ if(!refract(wt, wo, N, eta)) {
+ d3_splat(wi, 0); /* TODO handle this case */
+ return 0;
+ }
+ fresnel = bsdf->fresnel;
+
+ cos_wo_N = d3_dot(wo, N);
+ SSF(fresnel_dielectric_dielectric_setup(fresnel, bsdf->eta_i, bsdf->eta_t));
+ rho1 = ssf_fresnel_eval(fresnel, cos_wo_N);
+
+ cos_wt_N = d3_dot(wt, d3_minus(tmp, N));
+ SSF(fresnel_dielectric_dielectric_setup(fresnel, bsdf->eta_t, bsdf->eta_i));
+ rho2 = ssf_fresnel_eval(fresnel, cos_wt_N);
+
+ dst = bsdf->thickness / cos_wt_N;
+ tau = exp(-bsdf->T * dst);
+
+ tau_sqr = tau * tau;
+ rho2_sqr = rho2 * rho2;
+
+ R = rho1 + (1-rho1) * (1-rho2) * (rho2 * tau_sqr) / (1 - rho2_sqr*tau_sqr);
+ T = (tau * (1 - rho1) * ( 1 - rho2)) / (1-tau_sqr*rho2_sqr);
+#ifndef NDEBUG
+ {
+ const double A = ((1-tau) * (1-rho1)) / (1-rho2*tau);
+ ASSERT(eq_eps(R + A + T, 1, 1.e-6)); /* Check energy conservation */
+ }
+#endif
+
+ *pdf = INF;
+
+ /* Importance sample the BTDF wrt R */
+ if(ssp_rng_canonical(rng) < R) { /* Sample the reflective part */
+ reflect(wi, wo, N);
+ return 1;
+ } else { /* Sample the transmissive part */
+ d3_minus(wi, wo);
+ return T;
+ }
+}
+
+static double
+thin_transparent_dielectric_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
+thin_transparent_dielectric_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_thin_transparent_dielectric = {
+ thin_transparent_dielectric_init,
+ thin_transparent_dielectric_release,
+ thin_transparent_dielectric_sample,
+ thin_transparent_dielectric_eval,
+ thin_transparent_dielectric_pdf,
+ sizeof(struct thin_transparent_dielectric),
+ ALIGNOF(struct thin_transparent_dielectric)
+};
+
+res_T
+ssf_thin_transparent_dielectric_setup
+ (struct ssf_bxdf* bxdf,
+ const double transmissivity,
+ const double eta_i,
+ const double eta_t,
+ const double thickness)
+{
+ struct thin_transparent_dielectric* bsdf;
+
+ if(!bxdf || thickness <= 0 || eta_i <= 0 || eta_t <= 0 || transmissivity < 0
+ || transmissivity > 1)
+ return RES_BAD_ARG;
+ if(!BXDF_TYPE_EQ(&bxdf->type, &ssf_thin_transparent_dielectric))
+ return RES_BAD_ARG;
+
+ bsdf = bxdf->data;
+
+ bsdf->T = transmissivity;
+ bsdf->thickness = thickness;
+ bsdf->eta_i = eta_i;
+ bsdf->eta_t = eta_t;
+ return RES_OK;
+}
+