ssol_material.c (22636B)
1 /* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com) 2 * Copyright (C) 2016, 2018 CNRS 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17 #include "ssol.h" 18 #include "ssol_c.h" 19 #include "ssol_device_c.h" 20 #include "ssol_material_c.h" 21 #include "ssol_spectrum_c.h" 22 23 #include <rsys/double2.h> 24 #include <rsys/double3.h> 25 #include <rsys/double33.h> 26 #include <rsys/float2.h> 27 #include <rsys/float3.h> 28 #include <rsys/float33.h> 29 #include <rsys/ref_count.h> 30 #include <rsys/rsys.h> 31 #include <rsys/mem_allocator.h> 32 33 #include <star/ssf.h> 34 35 #include <math.h> 36 #include <omp.h> 37 38 /******************************************************************************* 39 * Helper functions 40 ******************************************************************************/ 41 /* Define if the submitted ssol_data are *certainly* equals or not. Note that it 42 * does not check explicitly the spectrum data since it would be too expensive; 43 * it compares their checksum and that's why one cannot certify that the data 44 * are strictly equals. Anyway, since this function is used to detect medium 45 * inconsistencies, it is actually really sufficient to use this strategy. */ 46 static INLINE int 47 ssol_data_ceq(const struct ssol_data* a, const struct ssol_data* b) 48 { 49 int i; 50 ASSERT(a && b); 51 52 if(a->type != b->type) { 53 i = 0; 54 } else { 55 switch(a->type) { 56 case SSOL_DATA_REAL: 57 i = a->value.real == b->value.real; 58 break; 59 case SSOL_DATA_SPECTRUM: 60 i = a->value.spectrum->checksum[0] == b->value.spectrum->checksum[0] 61 && a->value.spectrum->checksum[1] == b->value.spectrum->checksum[1]; 62 break; 63 default: FATAL("Unreachable code\n"); break; 64 } 65 } 66 return i; 67 } 68 69 static void 70 shade_normal_default 71 (struct ssol_device* dev, 72 struct ssol_param_buffer* buf, 73 const double wlen, 74 const struct ssol_surface_fragment* frag, 75 double* val) /* Returned value */ 76 { 77 ASSERT(frag && val); 78 (void)dev, (void)buf, (void)wlen; 79 d3_set(val, frag->Ns); 80 } 81 82 static res_T 83 create_dielectric_bsdf 84 (const struct ssol_material* mtl, 85 const struct ssol_surface_fragment* fragment, 86 const double wavelength, /* In nanometer */ 87 const struct ssol_medium* medium, 88 struct ssf_bsdf** bsdf) 89 { 90 double eta_i, eta_t; 91 const int ithread = omp_get_thread_num(); 92 res_T res = RES_OK; 93 ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC); 94 ASSERT(medium && bsdf); 95 (void)wavelength, (void)fragment; 96 97 if(!media_ceq(medium, &mtl->out_medium)) { 98 log_error(mtl->dev, "Inconsistent medium description.\n"); 99 res = RES_BAD_OP; 100 goto error; 101 } 102 103 eta_i = ssol_data_get_value(&mtl->out_medium.refractive_index, wavelength); 104 eta_t = ssol_data_get_value(&mtl->in_medium.refractive_index, wavelength); 105 106 #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 107 CALL(ssf_bsdf_create(&mtl->dev->bsdf_allocators[ithread], 108 &ssf_specular_dielectric_dielectric_interface, bsdf)); 109 CALL(ssf_specular_dielectric_dielectric_interface_setup(*bsdf, eta_i, eta_t)); 110 #undef CALL 111 112 exit: 113 return res; 114 error: 115 if(*bsdf) SSF(bsdf_ref_put(*bsdf)), bsdf = NULL; 116 goto exit; 117 } 118 119 static res_T 120 create_matte_bsdf 121 (const struct ssol_material* mtl, 122 const struct ssol_surface_fragment* fragment, 123 const double wavelength, /* In nanometer */ 124 struct ssf_bsdf** bsdf) 125 { 126 double reflectivity; 127 const int ithread = omp_get_thread_num(); 128 res_T res; 129 ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_MATTE); 130 ASSERT(bsdf); 131 132 /* Fetch material attribs */ 133 mtl->data.matte.reflectivity 134 (mtl->dev, mtl->buf, wavelength, fragment, &reflectivity); 135 136 /* Setup the BRDF */ 137 res = ssf_bsdf_create 138 (&mtl->dev->bsdf_allocators[ithread], &ssf_lambertian_reflection, bsdf); 139 if(res != RES_OK) goto error; 140 res = ssf_lambertian_reflection_setup(*bsdf, reflectivity); 141 if(res != RES_OK) goto error; 142 143 exit: 144 return res; 145 error: 146 if(*bsdf) SSF(bsdf_ref_put(*bsdf)), bsdf = NULL; 147 goto exit; 148 } 149 150 static res_T 151 create_mirror_bsdf 152 (const struct ssol_material* mtl, 153 const struct ssol_surface_fragment* fragment, 154 const double wavelength, /* In nanometer */ 155 const int rendering, 156 struct ssf_bsdf** bsdf) 157 { 158 struct ssf_fresnel* fresnel = NULL; 159 struct ssf_microfacet_distribution* distrib = NULL; 160 double roughness; 161 double reflectivity; 162 const int ithread = omp_get_thread_num(); 163 res_T res; 164 ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_MIRROR); 165 ASSERT(bsdf); 166 167 /* Fetch material attribs */ 168 mtl->data.mirror.reflectivity 169 (mtl->dev, mtl->buf, wavelength, fragment, &reflectivity); 170 mtl->data.mirror.roughness 171 (mtl->dev, mtl->buf, wavelength, fragment, &roughness); 172 173 /* Setup the fresnel term */ 174 res = ssf_fresnel_create(mtl->dev->allocator, &ssf_fresnel_constant, &fresnel); 175 if(res != RES_OK) goto error; 176 res = ssf_fresnel_constant_setup(fresnel, reflectivity); 177 if(res != RES_OK) goto error; 178 179 /* Setup the BRDF */ 180 if(roughness == 0) { /* Purely specular reflection */ 181 res = ssf_bsdf_create 182 (&mtl->dev->bsdf_allocators[ithread], &ssf_specular_reflection, bsdf); 183 if(res != RES_OK) goto error; 184 res = ssf_specular_reflection_setup(*bsdf, fresnel); 185 if(res != RES_OK) goto error; 186 } else { /* Glossy reflection */ 187 switch(mtl->data.mirror.distrib) { 188 /* Setup the microfacet distribution */ 189 case SSOL_MICROFACET_BECKMANN: 190 res = ssf_microfacet_distribution_create 191 (mtl->dev->allocator, &ssf_beckmann_distribution, &distrib); 192 if(res != RES_OK) goto error; 193 res = ssf_beckmann_distribution_setup(distrib, roughness); 194 if(res != RES_OK) goto error; 195 break; 196 case SSOL_MICROFACET_PILLBOX: 197 res = ssf_microfacet_distribution_create 198 (mtl->dev->allocator, &ssf_pillbox_distribution, &distrib); 199 if(res != RES_OK) goto error; 200 res = ssf_pillbox_distribution_setup(distrib, roughness); 201 if(res != RES_OK) goto error; 202 break; 203 default: FATAL("Unreachable code.\n"); break; 204 } 205 206 /* Microfacet2 is not well suited for rendering since it cannot be 207 * evaluated and consequently it returns an invalid result for direct 208 * lighting. */ 209 if(rendering) { 210 res = ssf_bsdf_create 211 (&mtl->dev->bsdf_allocators[ithread], &ssf_microfacet_reflection, bsdf); 212 } else { 213 res = ssf_bsdf_create 214 (&mtl->dev->bsdf_allocators[ithread], &ssf_microfacet2_reflection, bsdf); 215 } 216 if(res != RES_OK) goto error; 217 res = ssf_microfacet_reflection_setup(*bsdf, fresnel, distrib); 218 if(res != RES_OK) goto error; 219 } 220 221 exit: 222 if(fresnel) SSF(fresnel_ref_put(fresnel)); 223 if(distrib) SSF(microfacet_distribution_ref_put(distrib)); 224 return res; 225 error: 226 if(*bsdf) SSF(bsdf_ref_put(*bsdf)), bsdf = NULL; 227 goto exit; 228 } 229 230 static res_T 231 create_thin_dielectric_bsdf 232 (const struct ssol_material* mtl, 233 const struct ssol_surface_fragment* fragment, 234 const double wavelength, /* In nanometer */ 235 struct ssf_bsdf** bsdf) 236 { 237 double thickness; 238 double absorption; 239 double eta_i; 240 double eta_t; 241 const int ithread = omp_get_thread_num(); 242 res_T res = RES_OK; 243 ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_THIN_DIELECTRIC); 244 ASSERT(bsdf); 245 (void)wavelength, (void)fragment; 246 247 eta_i = ssol_data_get_value(&mtl->out_medium.refractive_index, wavelength); 248 eta_t = ssol_data_get_value 249 (&mtl->data.thin_dielectric.slab_medium.refractive_index, wavelength); 250 /* Here extinction is absorption only */ 251 absorption = ssol_data_get_value 252 (&mtl->data.thin_dielectric.slab_medium.extinction, wavelength); 253 thickness = mtl->data.thin_dielectric.thickness; 254 255 /* Setup the BxDF */ 256 res = ssf_bsdf_create 257 (&mtl->dev->bsdf_allocators[ithread], &ssf_thin_specular_dielectric, bsdf); 258 if(res != RES_OK) goto error; 259 res = ssf_thin_specular_dielectric_setup 260 (*bsdf, absorption, eta_i, eta_t, thickness); 261 if(res != RES_OK) goto error; 262 263 exit: 264 return res; 265 error: 266 if(*bsdf) SSF(bsdf_ref_put(*bsdf)), bsdf = NULL; 267 goto exit; 268 } 269 270 static INLINE int 271 check_shader_dielectric(const struct ssol_dielectric_shader* shader) 272 { 273 return shader && shader->normal; 274 } 275 276 static INLINE int 277 check_shader_mirror(const struct ssol_mirror_shader* shader) 278 { 279 return shader 280 && shader->normal 281 && shader->reflectivity 282 && shader->roughness; 283 } 284 285 static INLINE int 286 check_shader_matte(const struct ssol_matte_shader* shader) 287 { 288 return shader 289 && shader->normal 290 && shader->reflectivity; 291 } 292 293 static INLINE int 294 check_shader_thin_differential(const struct ssol_thin_dielectric_shader* shader) 295 { 296 return shader && shader->normal; 297 } 298 299 static INLINE int 300 check_medium(const struct ssol_medium* medium) 301 { 302 if(!medium) return 0; 303 304 /* Check extinction in [0, INF) */ 305 switch(medium->extinction.type) { 306 case SSOL_DATA_REAL: 307 if(medium->extinction.value.real < 0) 308 return 0; 309 break; 310 case SSOL_DATA_SPECTRUM: 311 if(!medium->extinction.value.spectrum 312 || !spectrum_check_data(medium->extinction.value.spectrum, 0, DBL_MAX)) 313 return 0; 314 break; 315 default: FATAL("Unreachable code\n"); break; 316 } 317 318 /* Check refractive index in ]0, INF) */ 319 switch(medium->refractive_index.type) { 320 case SSOL_DATA_REAL: 321 if(medium->refractive_index.value.real <= 0) 322 return 0; 323 break; 324 case SSOL_DATA_SPECTRUM: 325 if(!medium->refractive_index.value.spectrum 326 || !spectrum_check_data 327 (medium->refractive_index.value.spectrum, DBL_EPSILON, DBL_MAX)) 328 return 0; 329 break; 330 default: FATAL("Unreachable code\n"); break; 331 } 332 333 return 1; 334 } 335 336 static void 337 material_release(ref_T* ref) 338 { 339 struct ssol_device* dev; 340 struct ssol_material* material = CONTAINER_OF(ref, struct ssol_material, ref); 341 ASSERT(ref); 342 dev = material->dev; 343 if(material->buf) SSOL(param_buffer_ref_put(material->buf)); 344 if(material->type == SSOL_MATERIAL_THIN_DIELECTRIC) { 345 ssol_medium_clear(&material->data.thin_dielectric.slab_medium); 346 } 347 ssol_medium_clear(&material->in_medium); 348 ssol_medium_clear(&material->out_medium); 349 ASSERT(dev && dev->allocator); 350 MEM_RM(dev->allocator, material); 351 SSOL(device_ref_put(dev)); 352 } 353 354 /******************************************************************************* 355 * Local functions 356 ******************************************************************************/ 357 static res_T 358 ssol_material_create 359 (struct ssol_device* dev, 360 struct ssol_material** out_material, 361 enum ssol_material_type type) 362 { 363 struct ssol_material* material = NULL; 364 res_T res = RES_OK; 365 if(!dev 366 || !out_material 367 || type >= SSOL_MATERIAL_TYPES_COUNT__) { 368 return RES_BAD_ARG; 369 } 370 371 material = (struct ssol_material*)MEM_CALLOC 372 (dev->allocator, 1, sizeof(struct ssol_material)); 373 if (!material) { 374 res = RES_MEM_ERR; 375 goto error; 376 } 377 378 SSOL(device_ref_get(dev)); 379 material->dev = dev; 380 ref_init(&material->ref); 381 material->type = type; 382 material->in_medium = SSOL_MEDIUM_VACUUM; 383 material->out_medium = SSOL_MEDIUM_VACUUM; 384 material->normal = shade_normal_default; 385 386 exit: 387 if (out_material) *out_material = material; 388 return res; 389 error: 390 if (material) { 391 SSOL(material_ref_put(material)); 392 material = NULL; 393 } 394 goto exit; 395 } 396 397 /******************************************************************************* 398 * Exported ssol_material functions 399 ******************************************************************************/ 400 res_T 401 ssol_material_ref_get(struct ssol_material* material) 402 { 403 if (!material) 404 return RES_BAD_ARG; 405 ASSERT(material->type < SSOL_MATERIAL_TYPES_COUNT__); 406 ref_get(&material->ref); 407 return RES_OK; 408 } 409 410 res_T 411 ssol_material_ref_put(struct ssol_material* material) 412 { 413 if (!material) 414 return RES_BAD_ARG; 415 ASSERT(material->type < SSOL_MATERIAL_TYPES_COUNT__); 416 ref_put(&material->ref, material_release); 417 return RES_OK; 418 } 419 420 res_T 421 ssol_material_get_type 422 (const struct ssol_material* mtl, enum ssol_material_type* type) 423 { 424 if(!mtl || !type) return RES_BAD_ARG; 425 *type = mtl->type; 426 return RES_OK; 427 } 428 429 res_T 430 ssol_material_set_param_buffer 431 (struct ssol_material* mtl, struct ssol_param_buffer* buf) 432 { 433 if(!mtl || !buf) return RES_BAD_ARG; 434 SSOL(param_buffer_ref_get(buf)); 435 mtl->buf = buf; 436 return RES_OK; 437 } 438 439 res_T 440 ssol_material_create_dielectric 441 (struct ssol_device* dev, struct ssol_material** out_material) 442 { 443 return ssol_material_create(dev, out_material, SSOL_MATERIAL_DIELECTRIC); 444 } 445 446 res_T 447 ssol_material_create_mirror 448 (struct ssol_device* dev, struct ssol_material** out_material) 449 { 450 return ssol_material_create(dev, out_material, SSOL_MATERIAL_MIRROR); 451 } 452 453 res_T 454 ssol_material_create_matte 455 (struct ssol_device* dev, struct ssol_material** out_material) 456 { 457 return ssol_material_create(dev, out_material, SSOL_MATERIAL_MATTE); 458 } 459 460 res_T 461 ssol_material_create_thin_dielectric 462 (struct ssol_device* dev, struct ssol_material** out_material) 463 { 464 return ssol_material_create(dev, out_material, SSOL_MATERIAL_THIN_DIELECTRIC); 465 } 466 467 res_T 468 ssol_dielectric_setup 469 (struct ssol_material* material, 470 const struct ssol_dielectric_shader* shader, 471 const struct ssol_medium* outside_medium, 472 const struct ssol_medium* inside_medium) 473 { 474 if(!material 475 || material->type != SSOL_MATERIAL_DIELECTRIC 476 || !check_shader_dielectric(shader) 477 || !check_medium(outside_medium) 478 || !check_medium(inside_medium)) 479 return RES_BAD_ARG; 480 material->data.dielectric.dummy = 1; 481 ssol_medium_copy(&material->out_medium, outside_medium); 482 ssol_medium_copy(&material->in_medium, inside_medium); 483 material->normal = shader->normal; 484 return RES_OK; 485 } 486 487 res_T 488 ssol_mirror_setup 489 (struct ssol_material* material, 490 const struct ssol_mirror_shader* shader, 491 const enum ssol_microfacet_distribution distrib) 492 { 493 if(!material 494 || material->type != SSOL_MATERIAL_MIRROR 495 || !check_shader_mirror(shader) 496 || (unsigned)distrib >= SSOL_MICROFACET_DISTRIBUTIONS_COUNT__) 497 return RES_BAD_ARG; 498 material->normal = shader->normal; 499 material->data.mirror.reflectivity = shader->reflectivity; 500 material->data.mirror.roughness = shader->roughness; 501 material->data.mirror.distrib = distrib; 502 return RES_OK; 503 } 504 505 res_T 506 ssol_matte_setup 507 (struct ssol_material* material, const struct ssol_matte_shader* shader) 508 { 509 if(!material 510 || material->type != SSOL_MATERIAL_MATTE 511 || !check_shader_matte(shader)) 512 return RES_BAD_ARG; 513 material->normal = shader->normal; 514 material->data.matte.reflectivity = shader->reflectivity; 515 return RES_OK; 516 } 517 518 res_T 519 ssol_thin_dielectric_setup 520 (struct ssol_material* material, 521 const struct ssol_thin_dielectric_shader* shader, 522 const struct ssol_medium* outside_medium, 523 const struct ssol_medium* slab_medium, 524 const double thickness) 525 { 526 if(!material 527 || material->type != SSOL_MATERIAL_THIN_DIELECTRIC 528 || !check_shader_thin_differential(shader) 529 || !check_medium(outside_medium) 530 || !check_medium(slab_medium) 531 || thickness < 0) 532 return RES_BAD_ARG; 533 ssol_medium_copy(&material->data.thin_dielectric.slab_medium, slab_medium); 534 material->data.thin_dielectric.thickness = thickness; 535 ssol_medium_copy(&material->out_medium, outside_medium); 536 ssol_medium_copy(&material->in_medium, outside_medium); 537 material->normal = shader->normal; 538 return RES_OK; 539 } 540 541 res_T 542 ssol_material_create_virtual 543 (struct ssol_device* dev, struct ssol_material** out_material) 544 { 545 return ssol_material_create(dev, out_material, SSOL_MATERIAL_VIRTUAL); 546 } 547 548 /******************************************************************************* 549 * Local functions 550 ******************************************************************************/ 551 void 552 surface_fragment_setup 553 (struct ssol_surface_fragment* fragment, 554 const double pos[3], 555 const double dir[3], 556 const double normal[3], 557 const struct s3d_primitive* primitive, 558 const float uv[2]) 559 { 560 struct s3d_attrib attr; 561 char has_texcoord, has_normal; 562 struct s3d_attrib uvs[3]; 563 struct s3d_attrib P[3]; 564 double duv1[2], duv2[2]; 565 double dP1[3], dP2[3]; 566 double det; 567 ASSERT(fragment && pos && dir && primitive && uv); 568 569 /* Assume that the submitted normal look forward the incoming dir */ 570 ASSERT(d3_dot(normal, dir) <= 0); 571 572 d3_set(fragment->dir, dir); /* Setup the incoming direction */ 573 d3_set(fragment->P, pos); /* Setup the surface position */ 574 d3_normalize(fragment->Ng, normal); /* Normalize the geometry normal */ 575 576 /* Retrieve the position of the triangle vertices */ 577 S3D(triangle_get_vertex_attrib(primitive, 0, S3D_POSITION, &P[0])); 578 S3D(triangle_get_vertex_attrib(primitive, 1, S3D_POSITION, &P[1])); 579 S3D(triangle_get_vertex_attrib(primitive, 2, S3D_POSITION, &P[2])); 580 581 /* Retrieve the tex coord */ 582 S3D(primitive_has_attrib(primitive, SSOL_TO_S3D_TEXCOORD, &has_texcoord)); 583 if (!has_texcoord) { 584 d2_set_f2(fragment->uv, uv); 585 uvs[0].type = uvs[1].type = uvs[2].type = S3D_FLOAT2; 586 uvs[0].usage = uvs[1].usage = uvs[2].usage = SSOL_TO_S3D_TEXCOORD; 587 f2(uvs[0].value, 1, 0); 588 f2(uvs[1].value, 0, 1); 589 f2(uvs[2].value, 0, 0); 590 } else { 591 S3D(primitive_get_attrib(primitive, SSOL_TO_S3D_TEXCOORD, uv, &attr)); 592 S3D(triangle_get_vertex_attrib(primitive, 0, SSOL_TO_S3D_TEXCOORD, &uvs[0])); 593 S3D(triangle_get_vertex_attrib(primitive, 1, SSOL_TO_S3D_TEXCOORD, &uvs[1])); 594 S3D(triangle_get_vertex_attrib(primitive, 2, SSOL_TO_S3D_TEXCOORD, &uvs[2])); 595 ASSERT(attr.type == S3D_FLOAT2); 596 d2_set_f2(fragment->uv, attr.value); 597 } 598 599 /* Compute the partial derivatives. */ 600 duv1[0] = uvs[1].value[0] - uvs[0].value[0]; 601 duv1[1] = uvs[1].value[1] - uvs[0].value[1]; 602 duv2[0] = uvs[2].value[0] - uvs[0].value[0]; 603 duv2[1] = uvs[2].value[1] - uvs[0].value[1]; 604 dP1[0] = P[1].value[0] - P[0].value[0]; 605 dP1[1] = P[1].value[1] - P[0].value[1]; 606 dP1[2] = P[1].value[2] - P[0].value[2]; 607 dP2[0] = P[2].value[0] - P[0].value[0]; 608 dP2[1] = P[2].value[1] - P[0].value[1]; 609 dP2[2] = P[2].value[2] - P[0].value[2]; 610 det = duv1[0]*duv2[1] - duv1[1]*duv2[0]; 611 if(det == 0) { /* Handle zero determinant */ 612 double basis[9]; 613 d33_basis(basis, fragment->Ng); 614 d3_set(fragment->dPdu, basis + 0); 615 d3_set(fragment->dPdv, basis + 3); 616 } else { 617 double a[3], b[3]; 618 d3_sub(fragment->dPdu, d3_muld(a, dP1, duv2[1]), d3_muld(b, dP2, duv1[1])); 619 d3_sub(fragment->dPdv, d3_muld(a, dP2, duv1[0]), d3_muld(b, dP1, duv2[0])); 620 d3_divd(fragment->dPdu, fragment->dPdu, det); 621 d3_divd(fragment->dPdv, fragment->dPdv, det); 622 } 623 624 /* Retrieve and normalize the shading normal in world space */ 625 S3D(primitive_has_attrib(primitive, SSOL_TO_S3D_NORMAL, &has_normal)); 626 if (!has_normal) { 627 d3_set(fragment->Ns, fragment->Ng); 628 } else { 629 float transform[12]; 630 float vec[3]; 631 632 S3D(primitive_get_attrib(primitive, SSOL_TO_S3D_NORMAL, uv, &attr)); 633 ASSERT(attr.type == S3D_FLOAT3); 634 635 S3D(primitive_get_transform(primitive, transform)); 636 /* Check that transform is not "identity" */ 637 if(!f3_eq(transform + 0, f3(vec, 1.f, 0.f, 0.f)) 638 && !f3_eq(transform + 3, f3(vec, 0.f, 1.f, 0.f)) 639 && !f3_eq(transform + 6, f3(vec, 0.f, 0.f, 1.f))) { 640 /* Transform the normal in world space, i.e. multiply it by the inverse 641 * transpose of the "object to world" primitive matrix. Since the affine 642 * part of the 3x4 transformation matrix does not influence the normal 643 * transformation, use the linear part only. */ 644 f33_invtrans(transform, transform); 645 f33_mulf3(attr.value, transform, attr.value); 646 } 647 d3_set_f3(fragment->Ns, attr.value); 648 d3_normalize(fragment->Ns, fragment->Ns); 649 650 /* Ensure that the fetched shading normal look forward the incoming dir */ 651 if(d3_dot(dir, fragment->Ns) > 0) { 652 d3_minus(fragment->Ns, fragment->Ns); 653 } 654 } 655 } 656 657 void 658 material_shade_normal 659 (const struct ssol_material* mtl, 660 const struct ssol_surface_fragment* frag, 661 const double wavelength, 662 double N[3]) 663 { 664 ASSERT(mtl && frag && N); 665 mtl->normal(mtl->dev, mtl->buf, wavelength, frag, N); 666 } 667 668 res_T 669 material_create_bsdf 670 (const struct ssol_material* mtl, 671 const struct ssol_surface_fragment* fragment, 672 const double wavelength, /* In nanometer */ 673 const struct ssol_medium* medium, 674 const int rendering, /* Is BSDF used for rendering */ 675 struct ssf_bsdf** bsdf) 676 { 677 res_T res = RES_OK; 678 ASSERT(mtl); 679 680 switch(mtl->type) { 681 case SSOL_MATERIAL_DIELECTRIC: 682 res = create_dielectric_bsdf 683 (mtl, fragment, wavelength, medium, bsdf); 684 break; 685 case SSOL_MATERIAL_MATTE: 686 res = create_matte_bsdf(mtl, fragment, wavelength, bsdf); 687 break; 688 case SSOL_MATERIAL_MIRROR: 689 res = create_mirror_bsdf(mtl, fragment, wavelength, rendering, bsdf); 690 break; 691 case SSOL_MATERIAL_THIN_DIELECTRIC: 692 res = create_thin_dielectric_bsdf(mtl, fragment, wavelength, bsdf); 693 break; 694 case SSOL_MATERIAL_VIRTUAL: /* Nothing to shade */ break; 695 default: FATAL("Unreachable code\n"); break; 696 } 697 return res; 698 } 699 700 res_T 701 material_get_next_medium 702 (const struct ssol_material* mtl, 703 const struct ssol_medium* medium, 704 struct ssol_medium* next_medium) 705 { 706 ASSERT(mtl && medium && next_medium); 707 switch(mtl->type) { 708 /* The material is an interface between 2 media */ 709 case SSOL_MATERIAL_DIELECTRIC: 710 if(media_ceq(&mtl->out_medium, medium)) { 711 ssol_medium_copy(next_medium, &mtl->in_medium); 712 } else { 713 ASSERT(media_ceq(&mtl->in_medium, medium)); 714 ssol_medium_copy(next_medium, &mtl->out_medium); 715 } 716 break; 717 /* The material is not an interface between 2 media */ 718 case SSOL_MATERIAL_MATTE: 719 case SSOL_MATERIAL_MIRROR: 720 case SSOL_MATERIAL_THIN_DIELECTRIC: 721 ssol_medium_copy(next_medium, medium); 722 break; 723 default: FATAL("Unreachable code\n"); break; 724 } 725 return RES_OK; 726 } 727 728 /* Define if the submitted media are *certainly* equals. Refer to the 729 * check_ssol_data for more details. */ 730 int 731 media_ceq(const struct ssol_medium* a, const struct ssol_medium* b) 732 { 733 ASSERT(a && b); 734 return ssol_data_ceq(&a->refractive_index, &b->refractive_index) 735 && ssol_data_ceq(&a->extinction, &b->extinction); 736 }