ssf_pillbox_distribution.c (4237B)
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_microfacet_distribution_c.h" 18 19 #include <rsys/double3.h> 20 #include <rsys/double33.h> 21 22 #include <star/ssp.h> 23 24 struct pillbox_distribution { 25 double sin2_theta_max; 26 }; 27 28 /******************************************************************************* 29 * Private functions 30 ******************************************************************************/ 31 static res_T 32 pillbox_distribution_init(struct mem_allocator* allocator, void* distrib) 33 { 34 ASSERT(distrib); 35 (void)allocator; 36 ((struct pillbox_distribution*)distrib)->sin2_theta_max = 1.0; 37 return RES_OK; 38 } 39 40 static void 41 pillbox_distribution_release(void* distrib) 42 { (void)distrib; } 43 44 static double 45 pillbox_distribution_eval(void* distrib, const double N[3], const double wh[3]) 46 { 47 struct pillbox_distribution* pillbox = distrib; 48 double cos2_theta_max; 49 double cos2_wh_N; 50 ASSERT(distrib && N && wh); 51 ASSERT(d3_is_normalized(wh) && d3_is_normalized(N)); 52 cos2_wh_N = d3_dot(N, wh); 53 cos2_wh_N *= cos2_wh_N; 54 cos2_theta_max = 1.0 - pillbox->sin2_theta_max; 55 /* if |wh.N| >= theta_max then 0 56 * <=> if cos(|wh.N|)^2 < cos(theta_max)^2 then 0 */ 57 return (cos2_wh_N >= cos2_theta_max) ? 1.0 / (PI - PI*cos2_theta_max) : 0.0; 58 } 59 60 static void 61 pillbox_distribution_sample 62 (void* distrib, 63 struct ssp_rng* rng, 64 const double N[3], 65 double wh[3], 66 double* pdf) 67 { 68 struct pillbox_distribution* pillbox = distrib; 69 double basis[9]; 70 double dir[3]; 71 double cos2_theta_max; 72 double phi, sin2_theta, sin_theta, cos_theta; 73 ASSERT(rng && wh && N); 74 ASSERT(d3_is_normalized(N)); 75 76 cos2_theta_max = 1 - pillbox->sin2_theta_max; 77 sin2_theta = ssp_rng_uniform_double(rng, 0, pillbox->sin2_theta_max); 78 sin_theta = sqrt(sin2_theta); 79 cos_theta = sqrt(1 - sin2_theta); 80 phi = ssp_rng_uniform_double(rng, 0, 2 * PI); 81 dir[0] = cos(phi) * sin_theta; 82 dir[1] = sin(phi) * sin_theta; 83 dir[2] = cos_theta; 84 d33_muld3(wh, d33_basis(basis, N), dir); 85 86 if(pdf) *pdf = cos_theta / (PI-PI*cos2_theta_max); 87 } 88 89 static double 90 pillbox_distribution_pdf(void* distrib, const double N[3], const double wh[3]) 91 { 92 struct pillbox_distribution* pillbox = distrib; 93 double cos_wh_N; 94 double cos2_wh_N; 95 double cos2_theta_max; 96 ASSERT(distrib && N && wh); 97 ASSERT(d3_is_normalized(wh) && d3_is_normalized(N)); 98 cos_wh_N = d3_dot(wh, N); 99 if(cos_wh_N < 0.0) return 0.0; 100 cos2_theta_max = 1 - pillbox->sin2_theta_max; 101 cos2_wh_N = cos_wh_N * cos_wh_N; 102 if(cos2_wh_N < cos2_theta_max) return 0.0; 103 return cos_wh_N / (PI-PI*cos2_theta_max); 104 } 105 106 /******************************************************************************* 107 * Exported symbols 108 ******************************************************************************/ 109 const struct ssf_microfacet_distribution_type ssf_pillbox_distribution = { 110 pillbox_distribution_init, 111 pillbox_distribution_release, 112 pillbox_distribution_sample, 113 pillbox_distribution_eval, 114 pillbox_distribution_pdf, 115 sizeof(struct pillbox_distribution), 116 ALIGNOF(struct pillbox_distribution) 117 }; 118 119 res_T 120 ssf_pillbox_distribution_setup 121 (struct ssf_microfacet_distribution* distrib, 122 const double roughness) 123 { 124 double sin2_theta_max; 125 if(!distrib || roughness <= 0 || roughness > 1) return RES_BAD_ARG; 126 if(!MICROFACET_DISTRIBUTION_TYPE_EQ(&distrib->type, &ssf_pillbox_distribution)) 127 return RES_BAD_ARG; 128 sin2_theta_max = sin(roughness); 129 sin2_theta_max *= sin2_theta_max; 130 ((struct pillbox_distribution*)distrib->data)->sin2_theta_max = sin2_theta_max; 131 return RES_OK; 132 } 133