solstice-solver

Solver library of the solstice app
git clone git://git.meso-star.com/solstice-solver.git
Log | Files | Refs | README | LICENSE

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 }