star-sf

Set of surface and volume scattering functions
git clone git://git.meso-star.fr/star-sf.git
Log | Files | Refs | README | LICENSE

ssf_thin_specular_dielectric.c (5160B)


      1 /* Copyright (C) 2016-2018, 2021-2025 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "ssf.h"
     17 #include "ssf_bsdf_c.h"
     18 #include "ssf_optics.h"
     19 
     20 #include <rsys/double3.h>
     21 #include <star/ssp.h>
     22 
     23 struct thin_specular_dielectric {
     24   struct ssf_fresnel* fresnel;
     25   double absorption; /* In [0, 1] */
     26   double eta_i; /* Refractive index of the incoming medium */
     27   double eta_t; /* Refractive index of the transmissive medium */
     28   double thickness;
     29 };
     30 
     31 /*******************************************************************************
     32  * Private functions
     33  ******************************************************************************/
     34 static res_T
     35 thin_specular_dielectric_init(struct mem_allocator* allocator, void* data)
     36 {
     37   struct thin_specular_dielectric* bsdf = data;
     38   ASSERT(bsdf);
     39   return ssf_fresnel_create
     40     (allocator, &ssf_fresnel_dielectric_dielectric, &bsdf->fresnel);
     41 }
     42 
     43 static void
     44 thin_specular_dielectric_release(void* data)
     45 {
     46   struct thin_specular_dielectric* bsdf = data;
     47   SSF(fresnel_ref_put(bsdf->fresnel));
     48 }
     49 
     50 static double
     51 thin_specular_dielectric_sample
     52   (void* data,
     53    struct ssp_rng* rng,
     54    const double wo[3],
     55    const double N[3],
     56    double wi[3],
     57    int* type,
     58    double* pdf)
     59 {
     60   struct thin_specular_dielectric* bsdf = data;
     61   struct ssf_fresnel* fresnel;
     62   double wt[3], tmp[3];
     63   double cos_wo_N, cos_wt_N;
     64   double dst;
     65   double eta; /* Ratio of eta_i / eta_t */
     66   double R, T;
     67   double rho1, rho2, rho2_sqr;
     68   double tau, tau_sqr;
     69   ASSERT(bsdf && rng && wi && N && wo);
     70   ASSERT(d3_is_normalized(wo) && d3_is_normalized(N));
     71   ASSERT(d3_dot(wo, N) > -1.e-6);
     72   (void)rng;
     73 
     74   eta = bsdf->eta_i / bsdf->eta_t;
     75   if(!refract(wt, wo, N, eta)) { /* Total reflection */
     76     reflect(wi, wo, N);
     77     if(pdf) *pdf = INF;
     78     if(type) *type = SSF_SPECULAR | SSF_REFLECTION;
     79     return 1;
     80   }
     81   fresnel = bsdf->fresnel;
     82 
     83   cos_wo_N = MMAX(0.0, d3_dot(wo, N));
     84   SSF(fresnel_dielectric_dielectric_setup(fresnel, bsdf->eta_i, bsdf->eta_t));
     85   rho1 = ssf_fresnel_eval(fresnel, cos_wo_N);
     86 
     87   cos_wt_N = MMAX(0.0, d3_dot(wt, d3_minus(tmp, N)));
     88   SSF(fresnel_dielectric_dielectric_setup(fresnel, bsdf->eta_t, bsdf->eta_i));
     89   rho2 = ssf_fresnel_eval(fresnel, cos_wt_N);
     90 
     91   dst = bsdf->thickness / cos_wt_N;
     92   tau = exp(-bsdf->absorption * dst);
     93 
     94   tau_sqr = tau * tau;
     95   rho2_sqr = rho2 * rho2;
     96 
     97   R = rho1 + (1-rho1) * (1-rho2) * (rho2 * tau_sqr) / (1 - rho2_sqr*tau_sqr);
     98   T = (tau * (1 - rho1) * ( 1 - rho2)) / (1-tau_sqr*rho2_sqr);
     99 #ifndef NDEBUG
    100   {
    101     const double A = ((1-tau) * (1-rho1)) / (1-rho2*tau);
    102     ASSERT(eq_eps(R + A + T, 1, 1.e-6)); /* Check energy conservation */
    103   }
    104 #endif
    105 
    106   if(pdf) *pdf = INF;
    107 
    108   /* Importance sample the BTDF wrt R */
    109   if(ssp_rng_uniform_double(rng, 0, R + T) < R) {
    110     reflect(wi, wo, N);
    111     if(type) *type = SSF_SPECULAR | SSF_REFLECTION;
    112   } else { /* Sample the transmissive part */
    113     d3_minus(wi, wo);
    114     if(type) *type = SSF_SPECULAR | SSF_TRANSMISSION;
    115   }
    116   if(bsdf->absorption == 0) {
    117     /* avoid numerical loss of energy if no absorption */
    118     return 1;
    119   } else {
    120     return R + T;
    121   }
    122 }
    123 
    124 static double
    125 thin_specular_dielectric_eval
    126   (void* bsdf, const double wo[3], const double N[3], const double wi[3])
    127 {
    128    (void)bsdf, (void)wo, (void)N, (void)wi;
    129    return 0.0;
    130 }
    131 
    132 static double
    133 thin_specular_dielectric_pdf
    134   (void* bsdf, const double wo[3], const double N[3], const double wi[3])
    135 {
    136   (void)bsdf, (void)wo, (void)N, (void)wi;
    137   return 0.0;
    138 }
    139 
    140 /*******************************************************************************
    141  * Exported symbols
    142  ******************************************************************************/
    143 const struct ssf_bsdf_type ssf_thin_specular_dielectric = {
    144   thin_specular_dielectric_init,
    145   thin_specular_dielectric_release,
    146   thin_specular_dielectric_sample,
    147   thin_specular_dielectric_eval,
    148   thin_specular_dielectric_pdf,
    149   sizeof(struct thin_specular_dielectric),
    150   ALIGNOF(struct thin_specular_dielectric)
    151 };
    152 
    153 res_T
    154 ssf_thin_specular_dielectric_setup
    155   (struct ssf_bsdf* bsdf,
    156    const double absorption,
    157    const double eta_i,
    158    const double eta_t,
    159    const double thickness)
    160 {
    161   struct thin_specular_dielectric* data;
    162 
    163   if(!bsdf || thickness <= 0 || eta_i <= 0 || eta_t <= 0 || absorption < 0
    164   || absorption > 1)
    165     return RES_BAD_ARG;
    166   if(!BSDF_TYPE_EQ(&bsdf->type, &ssf_thin_specular_dielectric))
    167     return RES_BAD_ARG;
    168 
    169   data = bsdf->data;
    170 
    171   data->absorption = absorption;
    172   data->thickness = thickness;
    173   data->eta_i = eta_i;
    174   data->eta_t = eta_t;
    175   return RES_OK;
    176 }
    177