sphor_ran_source.c (8806B)
1 /* Copyright (C) 2024-2026 Centre National de la Recherche Scientifique 2 * Copyright (C) 2024-2026 Clermont Auvergne INP 3 * Copyright (C) 2024-2026 INSA Lyon 4 * Copyright (C) 2024-2026 Institut Mines Télécom Albi-Carmaux 5 * Copyright (C) 2024-2026 Institut National Polytechnique de Toulouse 6 * Copyright (C) 2024-2026 |Méso|Star> (contact@meso-star.com) 7 * Copyright (C) 2024-2026 PhotonLyX (info@photonlyx.com) 8 * Copyright (C) 2024-2026 Université de Lorraine 9 * Copyright (C) 2024-2026 Université Paul Sabatier 10 * Copyright (C) 2024-2026 Université Toulouse - Jean Jaurès 11 * 12 * This program is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation, either version 3 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 24 25 #include "sphor_c.h" 26 #include "sphor_interface.h" 27 #include "sphor_ran_geometry.h" 28 #include "sphor_ran_source.h" 29 #include "sphor_sources.h" 30 31 #include <rsys/double3.h> 32 #include <rsys/float3.h> 33 #include <rsys/math.h> 34 #include <rsys/rsys.h> 35 #include <star/s3d.h> 36 #include <star/sphin.h> 37 #include <star/ssp.h> 38 39 #include <math.h> 40 41 static double* 42 ran_hemisphere_cos_pow_n 43 (struct ssp_rng* rng, 44 double n, 45 double normal[3], 46 double sample[3], 47 double* pdf) 48 { 49 double phi = 0; 50 double tmp = 0; 51 double cos_theta = 0; 52 double sin_theta = 0; 53 double basis[9] = {0}; 54 double sample_local[3] = {0}; 55 56 ASSERT(NULL != rng); 57 ASSERT(NULL != normal); 58 ASSERT(NULL != sample); 59 ASSERT(d3_is_normalized(normal)); 60 ASSERT(0 <= n); 61 62 phi = ssp_rng_uniform_double(rng, 0, 2 * PI); 63 tmp = ssp_rng_canonical(rng); 64 65 cos_theta = pow(tmp, 1 / (n + 2)); 66 sin_theta = sqrt(1- cos_theta * cos_theta); 67 68 if(pdf) *pdf = (n+2) * pow(cos_theta, n + 1) / (2 * PI); 69 70 sample_local[0] = sin_theta * cos(phi); 71 sample_local[1] = sin_theta * sin(phi); 72 sample_local[2] = cos_theta; 73 74 return d33_muld3(sample, d33_basis(basis, normal), sample_local); 75 } 76 77 /******************************************************************************* 78 * Local functions 79 ******************************************************************************/ 80 res_T 81 source_surface_sample_direction 82 (struct sphor* sphor, 83 struct ssp_rng* rng, 84 struct primitive_pos* prim_pos, 85 double dir[3]) 86 { 87 struct sphin_source_surface* source = NULL; 88 struct sphin_source_surface_direction_distribution src_dir_dist 89 = SPHIN_SOURCE_SURFACE_DIRECTION_DISTRIBUTION_NULL; 90 double normal[3] = {0}; 91 double sample[3] = {0}; 92 res_T res = RES_OK; 93 94 res = primitive_get_source(sphor, &prim_pos->primitive, &source); 95 if (RES_OK != res) { goto error; } 96 if (NULL == source){ res = RES_BAD_ARG; goto error; } 97 98 /* Sample a direction according to the source direction distribution */ 99 res = sphin_source_surface_get_direction_distribution(source, &src_dir_dist); 100 if (RES_OK != res) { goto error; } 101 102 /* Determine the effective emission normal. 103 * Since scene geometry normals can be arbitrary, we use the pre-calculated 104 * 'side' property of the primitive obtained during positiob sampling to 105 * ensure the normal points into the emission hemisphere. */ 106 if (SPHIN_SIDE_FRONT == prim_pos->primitive.side) { 107 /* Normal aligns with emission. Use as us */ 108 d3_set(normal, prim_pos->primitive.normal); 109 } else if (SPHIN_SIDE_BACK == prim_pos->primitive.side) { 110 /* Normal oposed to emission. Invert it */ 111 d3_minus(normal, prim_pos->primitive.normal); 112 } else { res = RES_BAD_ARG; goto error; } 113 114 switch (src_dir_dist.type) { 115 case SPHIN_SOURCE_DIRECTION_COLLIM: 116 d3_set(sample, normal); 117 break; 118 case SPHIN_SOURCE_DIRECTION_COS_POW_N: 119 ran_hemisphere_cos_pow_n 120 (rng, src_dir_dist.cos_pow_n.collimation_degree, normal, sample, NULL); 121 break; 122 case SPHIN_SOURCE_DIRECTION_ISOTROPIC: 123 ssp_ran_hemisphere_cos(rng, normal, sample, NULL); 124 break; 125 case SPHIN_SOURCE_DIRECTION_NONE__: 126 res = RES_BAD_ARG; 127 goto error; 128 default: FATAL("Unreachable code\n"); break; 129 } 130 d3_normalize(dir, sample); 131 exit: 132 return res; 133 error: 134 goto exit; 135 } 136 137 res_T 138 sample_source_view 139 (struct sphor* sphor, 140 struct ssp_rng* rng, 141 struct source_view** source_view) 142 { 143 struct source_view* source_views = NULL; 144 size_t isource_view = 0; 145 146 ASSERT(NULL != sphor); 147 ASSERT(NULL != rng); 148 149 isource_view = ssp_ranst_discrete_get(rng, sphor->source_distrib_power); 150 151 source_views = darray_source_view_data_get(&sphor->source_views); 152 *source_view = &source_views[isource_view]; 153 154 return RES_OK; 155 } 156 157 res_T 158 sample_source_position 159 (struct sphor* sphor, 160 struct ssp_rng* rng, 161 struct source_view** source_view, 162 struct primitive_pos* position, 163 double pos[3]) 164 { 165 float st[2] = {0}; 166 size_t scn_prim_id; 167 struct source_view* source_to_sample = NULL; 168 struct s3d_primitive src_prim = S3D_PRIMITIVE_NULL; 169 struct s3d_primitive scn_prim = S3D_PRIMITIVE_NULL; 170 struct s3d_attrib src_normal; 171 struct s3d_attrib scn_normal; 172 struct s3d_attrib scn_pos; 173 res_T res = RES_OK; 174 175 ASSERT(NULL != sphor); 176 ASSERT(NULL != rng); 177 178 res = sample_source_view(sphor, rng, &source_to_sample); 179 if (RES_OK != res) { goto error; } 180 181 res = sample_surface_position 182 (sphor, rng, source_to_sample->view, &src_prim, st); 183 if (RES_OK != res) { goto error; } 184 185 scn_prim_id = darray_size_t_data_get 186 (&source_to_sample->src2scn)[src_prim.prim_id]; 187 188 res = s3d_scene_view_get_primitive 189 (sphor->scene_view, (unsigned)scn_prim_id, &scn_prim); 190 if (RES_OK != res) { goto error; } 191 192 res = s3d_primitive_get_attrib 193 (&src_prim, S3D_GEOMETRY_NORMAL, st, &src_normal); 194 if (RES_OK != res) { goto error; } 195 res = s3d_primitive_get_attrib 196 (&scn_prim, S3D_GEOMETRY_NORMAL, st, &scn_normal); 197 if (RES_OK != res) { goto error; } 198 199 f3_normalize(src_normal.value, src_normal.value); 200 f3_normalize(scn_normal.value, scn_normal.value); 201 202 /* S3D and star-phor use different conventions. 203 * Flip the normal in Star-Phor convention, i.e., right-hand rule */ 204 f3_minus(src_normal.value, src_normal.value); 205 f3_minus(scn_normal.value, scn_normal.value); 206 207 position->uv[0] = st[0]; 208 position->uv[1] = st[1]; 209 position->primitive.prim_id = scn_prim_id; 210 position->primitive.interface = 211 darray_interface_data_get(&sphor->interfaces) + scn_prim_id; 212 position->primitive.normal[0] = scn_normal.value[0]; 213 position->primitive.normal[1] = scn_normal.value[1]; 214 position->primitive.normal[2] = scn_normal.value[2]; 215 216 /* During setup, source geometry normals are explicitly oriented toward the 217 * emission direction. Scene geometry normals, however, are arbitrarily 218 * oriented based on initial triangle traversal. 219 * 220 * By calculating the dot product of these two normals, we determine if the 221 * scene's face orientation aligns with the emission direction, allowing us 222 * to label the emitting side */ 223 224 if (f3_dot(src_normal.value, scn_normal.value) > 0) { 225 /* The scene normal points in the same direction of the source normal, that 226 * points on the emission direction, i.e., */ 227 position->primitive.side = SPHIN_SIDE_FRONT; 228 } else { 229 position->primitive.side = SPHIN_SIDE_BACK; 230 } 231 232 res = s3d_primitive_get_attrib(&scn_prim, S3D_POSITION, st, &scn_pos); 233 if (RES_OK != res) { goto error; } 234 235 d3_set_f3(pos, scn_pos.value); 236 *source_view = source_to_sample; 237 exit: 238 return res; 239 error: 240 goto exit; 241 } 242 243 res_T 244 source_sample_wavelength 245 (struct sphor* sphor, 246 struct ssp_rng* rng, 247 struct source_view* source_view, 248 double* wavelength) 249 { 250 struct sphin_surface* surface = NULL; 251 struct sphin_source_surface* source = NULL; 252 struct sphin_source_surface_flux_density flux_density 253 = SPHIN_SOURCE_SURFACE_FLUX_DENSITY_NULL; 254 res_T res = RES_OK; 255 256 ASSERT(NULL != sphor); 257 ASSERT(NULL != rng); 258 ASSERT(NULL != source_view); 259 260 res = sphin_config_get_surface 261 (sphor->config, source_view->sphin_id, &surface); 262 if (RES_OK != res) { goto error; } 263 264 res = sphin_surface_get_source(surface, &source); 265 if (RES_OK != res) { goto error; } 266 if (NULL == source){ res = RES_BAD_ARG; goto error; } 267 268 res = sphin_source_surface_get_flux_density(source, &flux_density); 269 if (RES_OK != res) { goto error; } 270 271 if (NULL != flux_density.emission_spectrum) { 272 *wavelength = ssp_ranst_piecewise_linear_get 273 (source_view->emission_spectrum_pdf, rng); 274 } 275 exit: 276 return res; 277 error: 278 goto exit; 279 }