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