sphor_ran_bsdf.c (18789B)
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_ran_bsdf.h" 27 #include "sphor_interface.h" 28 29 #include <rsys/rsys.h> 30 #include <star/ssf.h> 31 32 /******************************************************************************* 33 * Internal BSDFs 34 ******************************************************************************/ 35 36 /* BTDF LAMBERTIAN */ 37 struct lambertian_transmission { 38 double transmissivity; 39 }; 40 41 static double 42 lambertian_transmission_eval 43 (void* data, 44 const double wo[3], 45 const double N[3], 46 const double wi[3]) 47 { 48 struct lambertian_transmission* btdf = data; 49 50 ASSERT(NULL != data); 51 ASSERT(NULL != N); 52 ASSERT(NULL != wi); 53 ASSERT(d3_is_normalized(N) && d3_is_normalized(wi)); 54 ASSERT(d3_dot(wi, N) < 0 && d3_dot(wo, N) > 0); 55 (void)wo, (void)N, (void)wi; 56 57 return btdf->transmissivity/ PI; 58 } 59 60 static double 61 lambertian_transmission_sample 62 (void* data, 63 struct ssp_rng* rng, 64 const double wo[3], 65 const double N[3], 66 double wi[3], 67 int* type, 68 double* pdf) 69 { 70 double sample[3]; 71 72 ASSERT(NULL != data); 73 ASSERT(NULL != rng); 74 ASSERT(NULL != N); 75 ASSERT(NULL != wi); 76 ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_dot(wo, N) > 0); 77 (void)wo; 78 79 ssp_ran_hemisphere_cos(rng, N, sample, pdf); 80 d3_muld(wi, sample, -1); 81 if (type) *type = SSF_TRANSMISSION | SSF_DIFFUSE; 82 return ((struct lambertian_transmission*)data)->transmissivity; 83 } 84 85 static double 86 lambertian_transmission_pdf 87 (void* data, 88 const double wo[3], 89 const double N[3], 90 const double wi[3]) 91 { 92 double cos_wi_N; 93 ASSERT(NULL != data); 94 ASSERT(NULL != N); 95 ASSERT(NULL != wi); 96 ASSERT(d3_is_normalized(N) && d3_is_normalized(wi)); 97 (void)data, (void)wo; 98 99 cos_wi_N = d3_dot(wi, N); 100 return cos_wi_N < 0.0 ? -cos_wi_N / PI : 0.0; 101 } 102 103 static res_T 104 lambertian_transmission_setup 105 (struct ssf_bsdf* bsdf, 106 const double transmissivity) 107 { 108 void* ptr = NULL; 109 110 ASSERT(NULL != bsdf); 111 ASSERT(0 <= transmissivity && transmissivity <=1); 112 113 ssf_bsdf_get_data(bsdf, &ptr); 114 struct lambertian_transmission* data = ptr; 115 data->transmissivity = transmissivity; 116 117 return RES_OK; 118 } 119 120 const struct ssf_bsdf_type lambertian_transmission = { 121 NULL, 122 NULL, 123 lambertian_transmission_sample, 124 lambertian_transmission_eval, 125 lambertian_transmission_pdf, 126 sizeof(struct lambertian_transmission), 127 ALIGNOF(struct lambertian_transmission) 128 }; 129 130 /* BTDF KEEP_CURRENT_DIR */ 131 struct keep_current_dir_transmission { 132 struct ssf_fresnel* fresnel; 133 }; 134 135 static void 136 keep_current_dir_transmission_release 137 (void* data) 138 { 139 struct keep_current_dir_transmission* btdf = data; 140 ASSERT(data); 141 if (btdf->fresnel) SSF(fresnel_ref_put(btdf->fresnel)); 142 } 143 144 static double 145 keep_current_dir_transmission_sample 146 (void* data, 147 struct ssp_rng* rng, 148 const double wo[3], 149 const double N[3], 150 double wi[3], 151 int* type, 152 double* pdf) 153 { 154 struct keep_current_dir_transmission* btdf = data; 155 double cos_wo_N; 156 157 ASSERT(NULL != data); 158 ASSERT(NULL != rng); 159 ASSERT(NULL != N); 160 ASSERT(NULL != wi); 161 ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_dot(wo, N) > 0); 162 (void)rng; 163 164 /* In ssf convention, wo points outwards the surface */ 165 d3_minus(wi, wo); 166 if (pdf) *pdf = INF; 167 if (type) *type = SSF_TRANSMISSION; 168 169 cos_wo_N = d3_dot(wo, N); 170 return 1 - ssf_fresnel_eval(btdf->fresnel, cos_wo_N); 171 } 172 173 static double 174 keep_current_dir_transmission_eval 175 (void* data, 176 const double wo[3], 177 const double N[3], 178 const double wi[3]) 179 { 180 (void)data, (void)wi, (void)N, (void)wo; 181 return 0.0; 182 } 183 184 static double 185 keep_current_dir_transmission_pdf 186 (void* data, 187 const double wo[3], 188 const double N[3], 189 const double wi[3]) 190 { 191 (void)data, (void)wi, (void)N, (void)wo; 192 return 0.0; 193 } 194 195 static res_T 196 keep_current_dir_transmission_setup 197 (struct ssf_bsdf* bsdf, 198 struct ssf_fresnel* fresnel) 199 { 200 void* ptr = NULL; 201 res_T res = RES_OK; 202 203 ASSERT(NULL != bsdf); 204 ASSERT(NULL != fresnel); 205 206 ssf_bsdf_get_data(bsdf, &ptr); 207 struct keep_current_dir_transmission* data = ptr; 208 209 if (data->fresnel != fresnel) { 210 if (NULL != data->fresnel) { 211 res = ssf_fresnel_ref_put(data->fresnel); 212 if (RES_OK != res) { return res; } 213 } 214 res = ssf_fresnel_ref_get(fresnel); 215 if (RES_OK != res) { return res; } 216 217 data->fresnel = fresnel; 218 } 219 220 return RES_OK; 221 } 222 223 const struct ssf_bsdf_type keep_current_dir_transmission = { 224 NULL, 225 keep_current_dir_transmission_release, 226 keep_current_dir_transmission_sample, 227 keep_current_dir_transmission_eval, 228 keep_current_dir_transmission_pdf, 229 sizeof(struct keep_current_dir_transmission), 230 ALIGNOF(struct keep_current_dir_transmission) 231 }; 232 233 /* BTDF SNELL DIELECTRIC */ 234 struct snell_dielectric_transmission { 235 struct ssf_fresnel* fresnel; 236 double eta_i; /* Refractive index of the incoming medium */ 237 double eta_t; /* Refractive index of the transmissive medium */ 238 }; 239 240 /* Refract the vect V wrt the normal N using the relative refractive index eta. 241 * Eta is the refraction index of the outside medium (where N points into) 242 * devided by the refraction index of the inside medium. By convention N and V 243 * points on the same side of the surface. */ 244 static INLINE void 245 refract(double res[3], const double V[3], const double N[3], const double eta) 246 { 247 double tmp0[3]; 248 double tmp1[3]; 249 double cos_theta_i; 250 double cos_theta_t; 251 double sin2_theta_i; 252 double sin2_theta_t; 253 254 ASSERT(res && V && N); 255 ASSERT(d3_is_normalized(V) && d3_is_normalized(N)); 256 cos_theta_i = d3_dot(V, N); 257 sin2_theta_i = MMAX(0, 1.0 - cos_theta_i*cos_theta_i); 258 sin2_theta_t = eta * eta * sin2_theta_i; 259 cos_theta_t = sqrt(1 - sin2_theta_t); 260 261 d3_muld(tmp0, V, eta); 262 d3_muld(tmp1, N, eta * cos_theta_i - cos_theta_t); 263 d3_sub(res, tmp1, tmp0); 264 } 265 266 static void 267 snell_dielectric_transmission_release 268 (void* data) 269 { 270 struct keep_current_dir_transmission* btdf = data; 271 ASSERT(data); 272 if (btdf->fresnel) SSF(fresnel_ref_put(btdf->fresnel)); 273 } 274 275 static double 276 snell_dielectric_transmission_sample 277 (void* data, 278 struct ssp_rng* rng, 279 const double wo[3], 280 const double N[3], 281 double wi[3], 282 int* type, 283 double* pdf) 284 { 285 struct snell_dielectric_transmission* btdf = data; 286 double wt[3]; 287 double cos_wo_N; 288 double eta; /* Ratio of eta_i / eta_t */ 289 290 ASSERT(NULL != btdf); 291 ASSERT(NULL != data); 292 ASSERT(NULL != rng); 293 ASSERT(NULL != N); 294 ASSERT(NULL != wi); 295 ASSERT(d3_is_normalized(wo) && d3_is_normalized(N)); 296 ASSERT(d3_dot(wo, N) > -1.e-6); 297 (void)rng; 298 299 eta = btdf->eta_i / btdf->eta_t; 300 refract(wt, wo, N, eta); 301 302 cos_wo_N = MMAX(0.0, d3_dot(wo, N)); 303 304 if(pdf) *pdf = INF; 305 d3_set(wi, wt); 306 if(type) *type = SSF_SPECULAR | SSF_TRANSMISSION; 307 return 1 - ssf_fresnel_eval(btdf->fresnel, cos_wo_N); 308 } 309 310 static double 311 snell_dielectric_transmission_eval 312 (void* bsdf, 313 const double wo[3], 314 const double N[3], 315 const double wi[3]) 316 { 317 (void)bsdf, (void)wo, (void)N, (void)wi; 318 return 0.0; 319 } 320 321 static double 322 snell_dielectric_transmission_pdf 323 (void* bsdf, 324 const double wo[3], 325 const double N[3], 326 const double wi[3]) 327 { 328 (void)bsdf, (void)wo, (void)N, (void)wi; 329 return 0.0; 330 } 331 332 static res_T 333 snell_dielectric_transmission_setup 334 (struct ssf_bsdf* bsdf, 335 struct ssf_fresnel* fresnel, 336 double eta_i, 337 double eta_t) 338 { 339 void* ptr = NULL; 340 res_T res = RES_OK; 341 342 ASSERT(NULL != bsdf); 343 344 ssf_bsdf_get_data(bsdf, &ptr); 345 struct snell_dielectric_transmission* data = ptr; 346 347 if (data->fresnel != fresnel) { 348 if (NULL != data->fresnel) { 349 res = ssf_fresnel_ref_put(data->fresnel); 350 if (RES_OK != res) { return res; } 351 } 352 res = ssf_fresnel_ref_get(fresnel); 353 if (RES_OK != res) { return res; } 354 355 data->fresnel = fresnel; 356 } 357 data->eta_i = eta_i; 358 data->eta_t = eta_t; 359 360 return RES_OK; 361 } 362 363 const struct ssf_bsdf_type snell_dielectric_transmission = { 364 NULL, 365 snell_dielectric_transmission_release, 366 snell_dielectric_transmission_sample, 367 snell_dielectric_transmission_eval, 368 snell_dielectric_transmission_pdf, 369 sizeof(struct snell_dielectric_transmission), 370 ALIGNOF(struct snell_dielectric_transmission) 371 }; 372 373 /******************************************************************************* 374 * Helper functions 375 ******************************************************************************/ 376 static res_T 377 setup_bsdf_reflection 378 (struct sphor* sphor, 379 struct ray* ray, 380 struct intersection* intersection, 381 double wavelength, 382 double reflectivity, 383 struct ssf_bsdf** out_bsdf) 384 { 385 struct primitive* primitive = &intersection->position.primitive; 386 struct sphin_brdf* brdf = NULL; 387 struct ssf_bsdf* bsdf = NULL; 388 struct ssf_fresnel* fresnel = NULL; 389 enum sphin_brdf_direction_distribution brdf_dir = SPHIN_BRDF_DIRECTION_NONE__; 390 391 res_T res = RES_OK; 392 393 ASSERT(NULL != sphor); 394 ASSERT(NULL != ray); 395 ASSERT(NULL != intersection); 396 397 (void)ray; 398 (void)wavelength; 399 400 primitive = (struct primitive*)&intersection->position.primitive; 401 402 res = primitive_get_brdf(sphor, primitive, &brdf); 403 if (RES_OK != res) { goto error; } 404 if (NULL == brdf) { res = RES_BAD_ARG; goto error; } 405 406 res = sphin_brdf_get_direction_distribution(brdf, &brdf_dir); 407 if (RES_OK != res) { goto error; } 408 409 switch (brdf_dir) { 410 case SPHIN_BRDF_DIRECTION_LAMBERT: 411 412 res = ssf_bsdf_create 413 (sphor->allocator, &ssf_lambertian_reflection, &bsdf); 414 if (RES_OK != res) { goto error; } 415 416 res = ssf_lambertian_reflection_setup(bsdf, reflectivity); 417 if (RES_OK != res) { goto error; } 418 419 break; 420 421 case SPHIN_BRDF_DIRECTION_SPECULAR: 422 res = ssf_bsdf_create 423 (sphor->allocator, &ssf_specular_reflection, &bsdf); 424 if (RES_OK != res) { goto error; } 425 426 res = ssf_fresnel_create 427 (sphor->allocator, &ssf_fresnel_constant, &fresnel); 428 if (RES_OK != res) { goto error; } 429 430 res = ssf_fresnel_constant_setup(fresnel, reflectivity); 431 if (RES_OK != res) { goto error; } 432 433 res = ssf_specular_reflection_setup(bsdf, fresnel); 434 if (RES_OK != res) { goto error; } 435 436 break; 437 438 case SPHIN_BRDF_DIRECTION_NONE__: 439 res = RES_BAD_ARG; 440 goto error; 441 442 default: 443 FATAL("Unreachable code\n"); 444 break; 445 } 446 447 exit: 448 if (NULL != fresnel) { SSF(fresnel_ref_put(fresnel)); } 449 *out_bsdf = bsdf; 450 error: 451 return res; 452 if (NULL != bsdf) { SSF(bsdf_ref_put(bsdf)); } 453 goto exit; 454 } 455 456 static res_T 457 setup_bsdf_transmission 458 (struct sphor* sphor, 459 struct ray* ray, 460 struct intersection* intersection, 461 double wavelength, 462 double transmissivity, 463 struct ssf_bsdf** out_bsdf) 464 { 465 double eta_i = 0, k_i = 0; 466 double eta_t = 0, k_t = 0; 467 struct primitive* primitive = &intersection->position.primitive; 468 struct sphin_btdf* btdf = NULL; 469 struct ssf_bsdf* bsdf = NULL; 470 struct ssf_fresnel* fresnel = NULL; 471 enum sphin_btdf_direction_distribution btdf_dir = SPHIN_BTDF_DIRECTION_NONE__; 472 473 res_T res = RES_OK; 474 475 ASSERT(NULL != sphor); 476 ASSERT(NULL != ray); 477 ASSERT(NULL != intersection); 478 479 (void)ray; 480 (void)wavelength; 481 482 primitive = (struct primitive*)&intersection->position.primitive; 483 484 res = primitive_get_btdf(sphor, primitive, &btdf); 485 if (RES_OK != res) { goto error; } 486 if (NULL == btdf) { res = RES_BAD_ARG; goto error; } 487 488 res = sphin_btdf_get_direction_distribution(btdf, &btdf_dir); 489 if (RES_OK != res) { goto error; } 490 491 switch (btdf_dir) { 492 case SPHIN_BTDF_DIRECTION_LAMBERT: 493 res = ssf_bsdf_create 494 (sphor->allocator, &lambertian_transmission, &bsdf); 495 if (RES_OK != res) { goto error; } 496 497 res = lambertian_transmission_setup(bsdf, transmissivity); 498 if (RES_OK != res) { goto error; } 499 500 break; 501 502 case SPHIN_BTDF_DIRECTION_KEEP_CURRENT_DIR: 503 res = ssf_bsdf_create 504 (sphor->allocator, &keep_current_dir_transmission, &bsdf); 505 if (RES_OK != res) { goto error; } 506 507 res = ssf_fresnel_create 508 (sphor->allocator, &ssf_fresnel_constant, &fresnel); 509 if (RES_OK != res) { goto error; } 510 511 res = ssf_fresnel_constant_setup(fresnel, 1 - transmissivity); 512 if (RES_OK != res) { goto error; } 513 514 res = keep_current_dir_transmission_setup(bsdf, fresnel); 515 if (RES_OK != res) { goto error; } 516 517 break; 518 519 case SPHIN_BTDF_DIRECTION_SNELL_DIELECTRIC: 520 521 res = ssf_bsdf_create 522 (sphor->allocator, &snell_dielectric_transmission, &bsdf); 523 if (RES_OK != res) { goto error; } 524 525 /* Get refractive index of the medium in the incident side */ 526 res = primitive_get_in_refraction_index 527 (sphor, primitive, wavelength, &eta_i, &k_i); 528 if (RES_OK != res) { goto error; } 529 530 /* Get refractive index of the medium in the transmitted side */ 531 res = primitive_get_out_refraction_index 532 (sphor, primitive, wavelength, &eta_t, &k_t); 533 534 res = ssf_fresnel_create 535 (sphor->allocator, &ssf_fresnel_constant, &fresnel); 536 if (RES_OK != res) { goto error; } 537 538 res = ssf_fresnel_constant_setup(fresnel, 1 - transmissivity); 539 if (RES_OK != res) { goto error; } 540 541 res = snell_dielectric_transmission_setup(bsdf, fresnel, eta_i, eta_t); 542 if (RES_OK != res) { goto error; } 543 544 break; 545 546 case SPHIN_BTDF_DIRECTION_NONE__: 547 res = RES_BAD_ARG; 548 goto error; 549 550 default: 551 FATAL("Unreachable code\n"); 552 break; 553 } 554 555 exit: 556 if (NULL != fresnel) { SSF(fresnel_ref_put(fresnel)); } 557 *out_bsdf = bsdf; 558 return res; 559 error: 560 if (NULL != bsdf) { SSF(bsdf_ref_put(bsdf)); } 561 goto exit; 562 } 563 564 static res_T 565 sample_interaction_type 566 (struct sphor* sphor, 567 struct ssp_rng* rng, 568 double reflectivity, 569 double transmissivity, 570 enum intersection_ray_interaction_type* interaction_type) 571 { 572 double s = 0; /* Random number */ 573 res_T res = RES_OK; 574 575 ASSERT(NULL != sphor); 576 ASSERT(NULL != rng); 577 578 (void)sphor; 579 580 if (reflectivity + transmissivity > 1) { 581 res = RES_BAD_ARG; goto error; } 582 583 if (reflectivity < 0) { 584 res = RES_BAD_ARG; goto error; } 585 586 if (transmissivity < 0) { 587 res = RES_BAD_ARG; goto error; } 588 589 s = ssp_rng_canonical(rng); 590 591 if (s >= 0 && s <= reflectivity) { 592 *interaction_type = INTERSECTION_RAY_INTERACTION_REFLECTION; 593 } else if (s > reflectivity && s <= reflectivity + transmissivity) { 594 *interaction_type = INTERSECTION_RAY_INTERACTION_TRANSMISSION; 595 } else if (s > reflectivity + transmissivity && s <= 1) { 596 *interaction_type = INTERSECTION_RAY_INTERACTION_ABSORPTION; 597 } else { res = RES_BAD_ARG; goto error; } 598 599 exit: 600 return res; 601 error: 602 goto exit; 603 } 604 605 /******************************************************************************* 606 * Local functions 607 ******************************************************************************/ 608 res_T 609 sample_interface_ray_interaction 610 (struct sphor* sphor, 611 struct ssp_rng* rng, 612 struct ray* ray, 613 struct intersection* intersection, 614 double wavelength, 615 double dir[3], 616 enum intersection_ray_interaction_type* interaction_type) 617 { 618 double invert_normal = 0; 619 double normal[3] = {0}; 620 double wo[3] = {0}; 621 double reflectivity = 0; 622 double transmissivity = 0; 623 double sample[3] = {0}; 624 struct primitive* primitive = NULL; 625 struct sphin_brdf* brdf = NULL; 626 struct sphin_btdf* btdf = NULL; 627 struct ssf_bsdf* bsdf = NULL; 628 res_T res = RES_OK; 629 630 ASSERT(NULL != sphor); 631 ASSERT(NULL != rng); 632 ASSERT(NULL != ray); 633 ASSERT(NULL != intersection); 634 635 primitive = &intersection->position.primitive; 636 637 /* Ensure that the ray direction and the geometry normal are compatible with 638 * the star-sf library. */ 639 d3_minus(wo, ray->direction); 640 d3_set(normal, primitive->normal); 641 /* Ensure normal and direction point to the same hemisphere */ 642 invert_normal = (d3_dot(normal, wo) > 0 ) ? 1. : -1.; 643 d3_normalize(normal, d3_muld(normal, normal, invert_normal)); 644 645 /* Verify if one of the surfaces defined in the intersected interface 646 * side has a BRDF defined */ 647 res = primitive_get_brdf(sphor, primitive, &brdf); 648 if (RES_OK != res) { goto error; } 649 650 /* Verify if one of the surfaces defined in the intersected interface 651 * side has a BTDF defined */ 652 res = primitive_get_btdf(sphor, primitive, &btdf); 653 if (RES_OK != res) { goto error; } 654 655 if (NULL == brdf && NULL == btdf) { /* Nothing to do */ 656 transmissivity = 1; 657 *interaction_type = INTERSECTION_RAY_INTERACTION_TRANSMISSION; 658 d3_set(dir, ray->direction); 659 } else { 660 /* Setup reflectivity */ 661 if (NULL != brdf) { 662 res = primitive_get_reflectivity 663 (sphor, primitive, wavelength, 664 d3_dot(normal, wo), &reflectivity); 665 if (RES_OK != res) { goto error; } 666 } 667 668 /* Setup transmissivity */ 669 if (NULL != btdf) { 670 res = primitive_get_transmissivity 671 (sphor, primitive, wavelength, 672 d3_dot(normal, wo), &transmissivity); 673 if (RES_OK != res) { goto error; } 674 } 675 676 /* Sample interaction type */ 677 res = sample_interaction_type 678 (sphor, rng, reflectivity, transmissivity, interaction_type); 679 if (RES_OK != res) { goto error; } 680 681 if (*interaction_type == INTERSECTION_RAY_INTERACTION_ABSORPTION) { 682 goto exit; /* Nothing else to do */ 683 } 684 685 if (*interaction_type == INTERSECTION_RAY_INTERACTION_REFLECTION) { 686 res = setup_bsdf_reflection 687 (sphor, ray, intersection, wavelength, reflectivity, &bsdf); 688 } 689 if (*interaction_type == INTERSECTION_RAY_INTERACTION_TRANSMISSION) { 690 res = setup_bsdf_transmission 691 (sphor, ray, intersection, wavelength, transmissivity, &bsdf); 692 } 693 ssf_bsdf_sample(bsdf, rng, wo, normal, sample, NULL, NULL); 694 695 d3_set(dir, sample); 696 } 697 698 exit: 699 if (NULL != bsdf) { SSF(bsdf_ref_put(bsdf)); } 700 return res; 701 error: 702 goto exit; 703 }