ssol_draw_pt.c (11443B)
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_c.h" 18 #include "ssol_atmosphere_c.h" 19 #include "ssol_camera.h" 20 #include "ssol_device_c.h" 21 #include "ssol_draw.h" 22 #include "ssol_material_c.h" 23 #include "ssol_object_c.h" 24 #include "ssol_ranst_sun_wl.h" 25 #include "ssol_scene_c.h" 26 #include "ssol_shape_c.h" 27 #include "ssol_sun_c.h" 28 29 #include <rsys/double2.h> 30 #include <rsys/double3.h> 31 #include <rsys/float3.h> 32 33 #include <star/s3d.h> 34 #include <star/ssf.h> 35 #include <star/ssp.h> 36 37 /******************************************************************************* 38 * Per thread draw_pt context 39 ******************************************************************************/ 40 struct thread_context { 41 struct ssp_rng* rng; 42 struct ranst_sun_wl* ran_wl; 43 float up[3]; 44 }; 45 46 static void 47 thread_context_release(struct thread_context* ctx) 48 { 49 ASSERT(ctx); 50 if(ctx->rng) SSP(rng_ref_put(ctx->rng)); 51 if(ctx->ran_wl) ranst_sun_wl_ref_put(ctx->ran_wl); 52 } 53 54 static res_T 55 thread_context_init 56 (struct mem_allocator* allocator, 57 struct thread_context* ctx) 58 { 59 ASSERT(ctx); 60 (void)allocator; 61 memset(ctx, 0, sizeof(ctx[0])); 62 return RES_OK; 63 } 64 65 static void 66 thread_context_setup 67 (struct thread_context* ctx, 68 struct ssp_rng* rng, 69 struct ranst_sun_wl* ran_wl, 70 const double up[3]) 71 { 72 ASSERT(ctx && rng && ran_wl && up); 73 if(ctx->rng) SSP(rng_ref_put(ctx->rng)); 74 SSP(rng_ref_get(rng)); 75 ranst_sun_wl_ref_get(ran_wl); 76 ctx->rng = rng; 77 ctx->ran_wl = ran_wl; 78 f3_set_d3(ctx->up, up); 79 } 80 81 /* Declare the container of the per thread contexts */ 82 #define DARRAY_NAME thread_context 83 #define DARRAY_DATA struct thread_context 84 #define DARRAY_FUNCTOR_INIT thread_context_init 85 #define DARRAY_FUNCTOR_RELEASE thread_context_release 86 #include <rsys/dynamic_array.h> 87 88 /******************************************************************************* 89 * Helper functions 90 ******************************************************************************/ 91 static INLINE double 92 sun_lighting 93 (struct ssol_sun* sun, 94 struct s3d_scene_view* view, 95 struct ray_data* ray_data, 96 struct ssf_bsdf* bsdf, 97 const double wo[3], 98 const double N[3], 99 const float ray_org[3]) 100 { 101 struct s3d_hit hit; 102 const float ray_range[2] = {0, FLT_MAX}; 103 double wi[3]; 104 float ray_dir[3]; 105 double cos_wi_N; 106 double R; 107 ASSERT(sun && view && ray_data && bsdf && wo && N && ray_org); 108 ASSERT(d3_dot(wo, N) >= 0); /* Assume that wo point outward the surface */ 109 110 /* Ensure that the incoming direction point outward the surface */ 111 d3_minus(wi, sun->direction); 112 113 /* The point look backward the sun */ 114 cos_wi_N = d3_dot(wi, N); 115 if(cos_wi_N < 0 || eq_eps(cos_wi_N, 0, 1.e-6)) return 0.0; 116 117 R = ssf_bsdf_eval(bsdf, wo, N, wi); 118 if(R <= 0) return 0.0; 119 120 f3_set_d3(ray_dir, wi); 121 S3D(scene_view_trace_ray(view, ray_org, ray_dir, ray_range, ray_data, &hit)); 122 if(S3D_HIT_NONE(&hit)) return R * cos_wi_N; 123 return 0; 124 } 125 126 static res_T 127 Li(struct ssol_scene* scn, 128 struct thread_context* ctx, 129 struct s3d_scene_view* view, 130 const float org[3], 131 const float dir[3], 132 double val[3]) 133 { 134 struct ssol_medium medium = SSOL_MEDIUM_VACUUM; 135 struct s3d_hit hit; 136 struct ray_data ray_data = RAY_DATA_NULL; 137 struct ssol_instance* inst; 138 struct ssol_material* mtl; 139 struct ssf_bsdf* bsdf = NULL; 140 const struct shaded_shape* sshape; 141 struct ssol_surface_fragment frag; 142 size_t isshape; 143 double throughput = 1.0; 144 double wi[3], o[3], uv[3]; 145 double wo[3]; 146 double N[3]; 147 double L = 0; 148 double R; 149 double pdf; 150 double cos_wi_Ng; 151 double wl; 152 const float ray_range[2] = {0, FLT_MAX}; 153 float ray_org[3]; 154 float ray_dir[3]; 155 enum ssol_side_flag side; 156 int russian_roulette = 0; 157 int type; 158 res_T res = RES_OK; 159 ASSERT(scn && view && org && dir && val); 160 161 ray_data.scn = scn; 162 ray_data.discard_virtual_materials = 1; 163 164 f3_set(ray_org, org); 165 f3_set(ray_dir, dir); 166 167 wl = ranst_sun_wl_get(ctx->ran_wl, ctx->rng); 168 169 if(scn->atmosphere) { 170 ssol_data_copy(&medium.extinction, &scn->atmosphere->extinction); 171 } 172 173 for(;;) { 174 double extinction; 175 S3D(scene_view_trace_ray 176 (view, ray_org, ray_dir, ray_range, &ray_data, &hit)); 177 178 if(S3D_HIT_NONE(&hit)) { /* Background lighting */ 179 if(f3_dot(ray_dir, ctx->up) > 0) L += throughput * 1.e-1; 180 break; 181 } 182 183 extinction = ssol_data_get_value(&medium.extinction, wl); 184 if(extinction > 0) { 185 throughput *= exp(-extinction * hit.distance); 186 if(throughput <= 0) break; 187 } 188 189 /* Retrieve the hit shaded shape */ 190 inst = *htable_instance_find(&scn->instances_rt, &hit.prim.inst_id); 191 isshape = *htable_shaded_shape_find 192 (&inst->object->shaded_shapes_rt, &hit.prim.geom_id); 193 sshape = darray_shaded_shape_cdata_get(&inst->object->shaded_shapes)+isshape; 194 195 d3_set_f3(o, ray_org); 196 d3_set_f3(wo, ray_dir); 197 d2_set_f2(uv, hit.uv); 198 d3_normalize(wo, wo); 199 200 /* Retrieve and normalized the hit normal */ 201 switch(sshape->shape->type) { 202 case SHAPE_MESH: 203 d3_normalize(N, d3_set_f3(N, hit.normal)); 204 break; 205 case SHAPE_PUNCHED: 206 d3_normalize(N, ray_data.N); 207 break; 208 default: FATAL("Unreachable code"); break; 209 } 210 211 if(d3_dot(N, wo) < 0) { 212 mtl = sshape->mtl_front; 213 side = SSOL_FRONT; 214 } else { 215 mtl = sshape->mtl_back; 216 side = SSOL_BACK; 217 d3_minus(N, N); 218 } 219 220 surface_fragment_setup(&frag, o, wo, N, &hit.prim, hit.uv); 221 material_shade_normal(mtl, &frag, wl, N); 222 223 /* Shaded normal may look backward the outgoing direction */ 224 if(d3_dot(N, wo) > 0) break; 225 226 if(bsdf) SSF(bsdf_ref_put(bsdf)), bsdf = NULL; 227 res = material_create_bsdf(mtl, &frag, wl, &medium, 1/*Rendering*/, &bsdf); 228 if(res != RES_OK) goto error; 229 230 /* Update the ray */ 231 ray_data.prim_from = hit.prim; 232 ray_data.inst_from = inst; 233 ray_data.side_from = side; 234 switch(sshape->shape->type) { 235 case SHAPE_MESH: f3_mulf(ray_dir, ray_dir, hit.distance); break; 236 case SHAPE_PUNCHED: f3_mulf(ray_dir, ray_dir, (float)ray_data.dst); break; 237 default: FATAL("Unreachable code"); break; 238 } 239 f3_add(ray_org, ray_org, ray_dir); 240 241 d3_minus(wo, wo); 242 if(scn->sun) { 243 L += throughput * sun_lighting 244 (scn->sun, view, &ray_data, bsdf, wo, N, ray_org); 245 } 246 247 /* Sampling a bounce direction */ 248 R = ssf_bsdf_sample(bsdf, ctx->rng, wo, N, wi, &type, &pdf); 249 ASSERT(0 <= R && R <= 1); 250 251 /* Due to the shading normal, the sampled direction may point in the wrong 252 * direction wrt the sampled BSDF component. */ 253 cos_wi_Ng = d3_dot(frag.Ng, wi); 254 if((cos_wi_Ng > 0 && (type & SSF_TRANSMISSION)) 255 || (cos_wi_Ng < 0 && (type & SSF_REFLECTION))) { 256 R = 0; 257 } 258 259 f3_set_d3(ray_dir, wi); 260 if(type & SSF_TRANSMISSION) material_get_next_medium(mtl, &medium, &medium); 261 262 if(!russian_roulette) { 263 throughput *= fabs(d3_dot(wi, N)) * R; 264 } else { 265 if(ssp_rng_canonical(ctx->rng) >= R) break; 266 throughput *= fabs(d3_dot(wi, N)); 267 } 268 269 if(throughput <= 0) break; 270 271 if(!russian_roulette) { 272 russian_roulette = throughput < 0.1; 273 } 274 } 275 d3_splat(val, L); 276 277 exit: 278 if(bsdf) SSF(bsdf_ref_put(bsdf)); 279 ssol_medium_clear(&medium); 280 return res; 281 error: 282 d3(val, 1, 1, 0); 283 goto exit; 284 } 285 286 static void 287 draw_pixel 288 (struct ssol_scene* scn, 289 const struct ssol_camera* cam, 290 struct s3d_scene_view* view, 291 const int ithread, 292 const size_t pix_coords[2], /* Image space pixel coordinates */ 293 const float pix_sz[2], /* Normalized pixel size */ 294 const size_t nsamples, 295 double pixel[3], 296 void* data) 297 { 298 struct darray_thread_context* thread_ctxs = data; 299 struct thread_context* ctx; 300 double sum[3] = {0, 0, 0}; 301 size_t isample; 302 res_T res = RES_OK; 303 ASSERT(scn && cam && pix_coords && pix_sz && nsamples && pixel && data); 304 ASSERT((size_t)ithread < darray_thread_context_size_get(thread_ctxs)); 305 306 ctx = darray_thread_context_data_get(thread_ctxs) + ithread; 307 308 FOR_EACH(isample, 0, nsamples) { 309 const int MAX_NFAILURES = 100; 310 double weight[3]; 311 float samp[2]; /* Pixel sample */ 312 float ray_org[3], ray_dir[3]; 313 int nfailures = 0; 314 315 /* Generate a sample into the pixel */ 316 samp[0] = ((float)pix_coords[0]+ssp_rng_canonical_float(ctx->rng))*pix_sz[0]; 317 samp[1] = ((float)pix_coords[1]+ssp_rng_canonical_float(ctx->rng))*pix_sz[1]; 318 319 do { 320 /* Generate a ray starting from the pinhole camera and passing through the 321 * pixel sample */ 322 camera_ray(cam, samp, ray_org, ray_dir); 323 324 /* Compute the radiance arriving through the sampled camera ray */ 325 res = Li(scn, ctx, view, ray_org, ray_dir, weight); 326 } while(res == RES_BAD_OP && ++nfailures < MAX_NFAILURES); 327 if(res != RES_OK) goto error; 328 329 d3_add(sum, sum, weight); 330 } 331 332 d3_divd(pixel, sum, (double)nsamples); 333 exit: 334 return; 335 error: 336 log_error(scn->dev, "Path tracing integrator error.\n"); 337 d3(pixel, 1, 1, 0); 338 goto exit; 339 } 340 341 /******************************************************************************* 342 * Exported function 343 ******************************************************************************/ 344 res_T 345 ssol_draw_pt 346 (struct ssol_scene* scn, 347 struct ssol_camera* cam, 348 const size_t width, 349 const size_t height, 350 const size_t spp, 351 const double up[3], 352 ssol_write_pixels_T writer, 353 void* data) 354 { 355 struct darray_thread_context thread_ctxs; 356 struct ssp_rng_proxy* rng_proxy = NULL; 357 struct ranst_sun_wl* ran_sun_wl = NULL; 358 size_t i; 359 res_T res = RES_OK; 360 361 if(!scn || !up) return RES_BAD_ARG; 362 363 darray_thread_context_init(scn->dev->allocator, &thread_ctxs); 364 365 res = scene_check(scn, FUNC_NAME); 366 if(res != RES_OK) goto error; 367 368 /* Create a RNG proxy */ 369 res = ssp_rng_proxy_create 370 (scn->dev->allocator, SSP_RNG_THREEFRY, scn->dev->nthreads, &rng_proxy); 371 if(res != RES_OK) goto error; 372 373 res = sun_create_wavelength_distribution(scn->sun, &ran_sun_wl); 374 if(res != RES_OK) goto error; 375 376 /* Create the thread contexts */ 377 res = darray_thread_context_resize(&thread_ctxs, scn->dev->nthreads); 378 if(res != RES_OK) goto error; 379 FOR_EACH(i, 0, scn->dev->nthreads) { 380 struct thread_context* ctx; 381 struct ssp_rng* rng; 382 383 ctx = darray_thread_context_data_get(&thread_ctxs)+i; 384 385 res = ssp_rng_proxy_create_rng(rng_proxy, i, &rng); 386 if(res != RES_OK) goto error; 387 388 thread_context_setup(ctx, rng, ran_sun_wl, up); 389 SSP(rng_ref_put(rng)); 390 } 391 392 /* Invoke the draw process */ 393 res = draw(scn, cam, width, height, spp, writer, data, draw_pixel, &thread_ctxs); 394 if(res != RES_OK) goto error; 395 396 exit: 397 darray_thread_context_release(&thread_ctxs); 398 if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); 399 if(ran_sun_wl) ranst_sun_wl_ref_put(ran_sun_wl); 400 return (res_T)res; 401 error: 402 goto exit; 403 }