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 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:
Mcmake/CMakeLists.txt | 3++-
Msrc/ssf.h | 13+++++++++++++
Msrc/ssf_optics.h | 31++++++++++++++++++++++++++++++-
Asrc/ssf_thin_transparent_dielectric.c | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +} +