sdis_brdf.c (3860B)
1 /* Copyright (C) 2016-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 "sdis.h" 17 #include "sdis_brdf.h" 18 #include "sdis_interface_c.h" 19 20 #include <star/ssp.h> 21 #include <rsys/double3.h> 22 23 /******************************************************************************* 24 * Helper functions 25 ******************************************************************************/ 26 static INLINE res_T 27 check_brdf_setup_args(const struct brdf_setup_args* args) 28 { 29 const struct sdis_medium* mdm = NULL; 30 31 if(!args) return RES_BAD_ARG; 32 33 if(args->interf == NULL || args->frag == NULL) 34 return RES_BAD_ARG; 35 36 switch(args->frag->side) { 37 case SDIS_FRONT: mdm = args->interf->medium_front; break; 38 case SDIS_BACK: mdm = args->interf->medium_back; break; 39 default: FATAL("Unreachable code\n"); break; 40 } 41 42 if(sdis_medium_get_type(mdm) != SDIS_FLUID) 43 return RES_BAD_ARG; 44 45 return RES_OK; 46 } 47 48 /* Reflect the V wrt the normal N. By convention V points outward the surface. 49 * In fact, this function is a double-precision version of the reflect_3d 50 * function. TODO Clean this "repeat" */ 51 static FINLINE double* 52 reflect(double res[3], const double V[3], const double N[3]) 53 { 54 double tmp[3]; 55 double cos_V_N; 56 ASSERT(res && V && N); 57 ASSERT(d3_is_normalized(V) && d3_is_normalized(N)); 58 cos_V_N = d3_dot(V, N); 59 d3_muld(tmp, N, 2*cos_V_N); 60 d3_sub(res, tmp, V); 61 return res; 62 } 63 64 /******************************************************************************* 65 * Local functions 66 ******************************************************************************/ 67 res_T 68 brdf_setup 69 (struct sdis_device* dev, 70 const struct brdf_setup_args* args, 71 struct brdf* brdf) 72 { 73 res_T res = RES_OK; 74 75 ASSERT(check_brdf_setup_args(args) == RES_OK); 76 77 #define GET(Attr) { \ 78 brdf->Attr = interface_side_get_##Attr \ 79 (args->interf, args->source_id, args->frag); \ 80 \ 81 res = interface_side_check_##Attr \ 82 (dev, brdf->Attr, args->frag->P, args->frag->time); \ 83 if(res != RES_OK) goto error; \ 84 } (void)0 85 86 GET(emissivity); 87 GET(specular_fraction); 88 89 #undef GET 90 91 exit: 92 return res; 93 error: 94 *brdf = BRDF_NULL; 95 goto exit; 96 } 97 98 void 99 brdf_sample 100 (const struct brdf* brdf, 101 struct ssp_rng* rng, 102 const double wi[3], /* Incident direction. Point away from the surface */ 103 const double N[3], /* Surface normal */ 104 struct brdf_sample* sample) 105 { 106 double r = 0; /* Random number */ 107 108 /* Preconditions */ 109 ASSERT(brdf && rng && wi && N && sample); 110 ASSERT(d3_is_normalized(wi) && d3_is_normalized(N)); 111 ASSERT(d3_dot(wi, N) > 0); 112 113 r = ssp_rng_canonical(rng); 114 115 /* Sample the specular part */ 116 if(r < brdf->specular_fraction) { 117 reflect(sample->dir, wi, N); 118 sample->pdf = 1; 119 sample->cpnt = BRDF_SPECULAR; 120 121 /* Sample the diffuse part */ 122 } else { 123 ssp_ran_hemisphere_cos(rng, N, sample->dir, NULL); 124 sample->pdf = 1.0/PI; 125 sample->cpnt = BRDF_DIFFUSE; 126 } 127 }