star-gf

Compute Gebhart factors
git clone git://git.meso-star.fr/star-gf.git
Log | Files | Refs | README | LICENSE

commit 10fbe01e1892bb1ad8c9f0f96caed8a11930ffee
parent 0dfae75c6705adbb51caba57462236719996e1ee
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  6 Jul 2016 17:33:00 +0200

Add support of Gebhart Factor integration on 2D scene

Make the gebhart_factor realisation generic to the dimensionality of the
submitted scene and instantiate it for both 2D and 3D scene. The 2D
scenes are handled through the Star-2D library.

Diffstat:
Mcmake/CMakeLists.txt | 5+++--
Msrc/sgf.h | 15+++++++++++++--
Msrc/sgf_estimator.c | 202++++++++++++++++---------------------------------------------------------------
Asrc/sgf_realisation.h | 340+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sgf_cube.c | 3++-
Msrc/test_sgf_tetrahedron.c | 3++-
6 files changed, 401 insertions(+), 167 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -27,6 +27,7 @@ find_package(RCMake 0.2.3 REQUIRED) find_package(RSys 0.3 REQUIRED) find_package(StarSP 0.3 REQUIRED) find_package(Star3D 0.3 REQUIRED) +find_package(Star2D REQUIRED) find_package(OpenMP 1.2 REQUIRED) include_directories( @@ -48,7 +49,7 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SGF_FILES_SRC sgf_device.c sgf_estimator.c) set(SGF_FILES_INC_API sgf.h) -set(SGF_FILES_INC sgf_device_c.h) +set(SGF_FILES_INC sgf_device_c.h sgf_realisation.h) set(SGF_FILES_DOC COPYING README.md) # Prepend each file in the `SGF_FILES_<SRC|INC>' list by `SGF_SOURCE_DIR' @@ -63,7 +64,7 @@ set_target_properties(sgf PROPERTIES VERSION ${VERSION} SOVERSION ${VERSION_MAJOR}) -target_link_libraries(sgf RSys Star3D StarSP) +target_link_libraries(sgf RSys Star2D Star3D StarSP) if(CMAKE_COMPILER_IS_GNUCC) target_link_libraries(sgf m) endif() diff --git a/src/sgf.h b/src/sgf.h @@ -48,6 +48,11 @@ enum sgf_material_property { SGF_MATERIAL_SPECULARITY }; +enum sgf_dimensionality { + SGF_2D, + SGF_3D +}; + /* Descriptor of the scene */ struct sgf_scene_desc { /* Retrieve material properties from client side memory */ @@ -58,11 +63,17 @@ struct sgf_scene_desc { const size_t spectral_band_id); /* Spectral band identifier */ void* material; /* Client side material */ size_t spectral_bands_count; /* Total number of spectral bands */ - struct s3d_scene* scene; /* Star-3D encapsulation of the geometry */ + + /* Star-3D encapsulation of the geometry */ + enum sgf_dimensionality dimensionality; + union { + struct s3d_scene* scn3d; + struct s2d_scene* scn2d; + } geometry; }; static const struct sgf_scene_desc SGF_SCENE_DESC_NULL = { - NULL, NULL, 0, 0 + NULL, NULL, 0, SGF_3D, { NULL } }; /* Estimated Gebart Factor between 2 faces */ diff --git a/src/sgf_estimator.c b/src/sgf_estimator.c @@ -17,6 +17,7 @@ #include "sgf.h" #include "sgf_device_c.h" +#include "sgf_realisation.h" #include <star/s3d.h> #include <star/ssp.h> @@ -31,18 +32,18 @@ enum status_type { INFINITY_RADIATIVE_FLUX }; -struct accum { - double radiative_flux; - double sqr_radiative_flux; -}; - +/* Generate the accum dynamic array data type */ #define DARRAY_NAME accum #define DARRAY_DATA struct accum #include <rsys/dynamic_array.h> -/* How many self intersections are tested on the same ray before an error - * occurs */ -#define NSELF_HITS_MAX 10 +/* Generate the 2D realisation function */ +#define SGF_DIMENSIONALITY 2 +#include "sgf_realisation.h" + +/* Generate the 3D realisation function */ +#define SGF_DIMENSIONALITY 3 +#include "sgf_realisation.h" /* Estimator of the Gebhart Factor of a given face to all the other ones */ struct sgf_estimator { @@ -60,19 +61,16 @@ struct sgf_estimator { /******************************************************************************* * Helper functions ******************************************************************************/ -static INLINE char +static INLINE int check_scene_desc(struct sgf_scene_desc* desc) { ASSERT(desc); - return desc->get_material_property && desc->scene; -} - -static FINLINE void -accum_weight(struct accum* accums, const size_t iface, const double weight) -{ - ASSERT(accums); - accums[iface].radiative_flux += weight; - accums[iface].sqr_radiative_flux += weight * weight; + if(!desc->get_material_property) return 0; + switch(desc->dimensionality) { + case SGF_2D: return desc->geometry.scn2d != NULL; + case SGF_3D: return desc->geometry.scn3d != NULL; + default: return 0; + } } static FINLINE void @@ -91,144 +89,6 @@ setup_status status->nsteps = nsteps; /* # realisations */ } -/* Return RES_BAD_OP on numerical issue. i.e. the radiative path is skipped */ -static res_T -gebhart_radiative_path - (struct sgf_device* dev, - struct accum* accums, - struct accum* accums_infinity, - struct ssp_rng* rng, - const float epsilon, - const size_t primitive_id, - struct sgf_scene_desc* desc) -{ - struct s3d_hit hit; - struct s3d_primitive prim_from, prim; - struct s3d_attrib attrib; - size_t nself_hits = 0; - const double trans_min = 1.e-8; /* Minimum transmissivity threshold */ - double proba_reflec_spec; - double transmissivity, emissivity, reflectivity, specularity; -#ifndef NDEBUG - double infinite_radiative_flux = 0.0; - double sum_radiative_flux = 0.f; -#endif - float st[2] = {0.f, 0.f}; /* Parametric coordinate of the sampled position */ - float vec0[3]; /* Temporary vector */ - float normal[3]; /* Geometric normal */ - float pos[3]; /* Radiative path position */ - float dir[4]; /* Radiative path direction. dir[3] <=> sampled dir pdf */ - float range[2]; /* Traced ray range */ - float u, v; - - ASSERT(accums && accums_infinity && epsilon > 0.f && rng && desc); - - /* Discard faces with no emissivity */ - emissivity = desc->get_material_property(desc->material, - SGF_MATERIAL_EMISSIVITY, primitive_id, 0); - if(emissivity <= 0.0) - return RES_OK; - - /* Get the geometric normal of the input primitive */ - S3D(scene_get_primitive(desc->scene, (unsigned)primitive_id, &prim)); - S3D(primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attrib)); - f3_normalize(normal, attrib.value); - - /* Uniformly sample the primitive to define the starting position of the - * radiative path. */ - u = ssp_rng_canonical_float(rng); - v = ssp_rng_canonical_float(rng); - S3D(primitive_sample(&prim, u, v, st)); - S3D(primitive_get_attrib(&prim, S3D_POSITION, st, &attrib)); - f3_set(pos, attrib.value); - - /* Cosine weighted sampling of the radiative path direction around `normal' */ - ssp_ran_hemisphere_cos(rng, normal, dir); - - transmissivity = 1.0; - for(;;) { /* Here we go */ - prim_from = prim; - range[0] = epsilon, range[1] = FLT_MAX; - - nself_hits = 0; - do { /* Handle auto intersection */ - S3D(scene_trace_ray(desc->scene, pos, dir, range, NULL, &hit)); - if(!S3D_HIT_NONE(&hit)) range[0] = MMAX(range[0], hit.distance); - range[0] = nextafterf(range[0], range[1]); - } while(S3D_PRIMITIVE_EQ(&hit.prim, &prim_from) /* Self hit */ - && ++nself_hits < NSELF_HITS_MAX); /* Too many self hits */ - - if(nself_hits >= NSELF_HITS_MAX) { - if(dev->verbose) { - logger_print(dev->logger, LOG_ERROR, - "Too many self hit on a given ray. Ray origin: %g, %g, %g\n", - SPLIT3(pos)); - } - return RES_BAD_OP; - } - - if(S3D_HIT_NONE(&hit)) { /* The ray is outside the volume */ - accum_weight(accums_infinity, prim.scene_prim_id, transmissivity); -#ifndef NDEBUG - infinite_radiative_flux = transmissivity; -#endif - break; - } - - /* Retrieve the hit position and the hit primitive id */ - f3_mulf(vec0, dir, hit.distance); - f3_add(pos, vec0, pos); - prim = hit.prim; - - /* Fetch material property */ - emissivity = desc->get_material_property(desc->material, - SGF_MATERIAL_EMISSIVITY, prim.scene_prim_id, 0); - specularity = desc->get_material_property(desc->material, - SGF_MATERIAL_SPECULARITY, prim.scene_prim_id, 0); - reflectivity = desc->get_material_property(desc->material, - SGF_MATERIAL_REFLECTIVITY, prim.scene_prim_id, 0); - - if(transmissivity > trans_min) { - const double weight = transmissivity * emissivity; - accum_weight(accums, prim.scene_prim_id, weight); - transmissivity = transmissivity * (1.0 - emissivity); -#ifndef NDEBUG - sum_radiative_flux += weight; -#endif - } else { - /* Russian roulette */ - if(ssp_rng_canonical(rng) < emissivity) { - const double weight = transmissivity; - accum_weight(accums, prim.scene_prim_id, weight); -#ifndef NDEBUG - sum_radiative_flux += weight; -#endif - break; - } - } - - if(reflectivity <= 0.0) break; - - proba_reflec_spec = specularity / reflectivity; - f3_normalize(normal, hit.normal); - - if(ssp_rng_canonical(rng) >= proba_reflec_spec) { /* Diffuse reflection */ - ssp_ran_hemisphere_cos(rng, normal, dir); - ASSERT(f3_dot(normal, dir) > 0); - } else { /* Specular reflection */ - const float tmp = -2.f * f3_dot(dir, normal); - f3_mulf(vec0, normal, tmp); - f3_add(dir, dir, vec0); - f3_normalize(dir, dir); - } - } -#if !defined(NDEBUG) - /* Check the energy conservation property */ - ASSERT(eq_eps(sum_radiative_flux + infinite_radiative_flux, 1.0, 1.e6)); -#endif - return RES_OK; -} - static res_T estimator_create(struct sgf_device* dev, struct sgf_estimator** out_estimator) { @@ -334,14 +194,18 @@ sgf_integrate const size_t steps_count, struct sgf_estimator** out_estimator) { + #define SXD_FUNC(Func) (desc->dimensionality == SGF_2D ? S2D(Func) : S3D(Func)) + #define SXD_ENUM(Enum) (desc->dimensionality == SGF_2D ? S2D_##Enum : S3D_##Enum) struct sgf_estimator* estimator = NULL; size_t istep; size_t ispectral_band; size_t nprims; float epsilon; float lower[3], upper[3]; + void* scene; int mask; res_T res = RES_OK; + gebhart_radiative_path_T gebhart_radiative_path; if(!dev || !steps_count || !rng || !desc || !check_scene_desc(desc) || !out_estimator) { @@ -352,19 +216,32 @@ sgf_integrate res = estimator_create(dev, &estimator); if(res != RES_OK) goto error; + switch(desc->dimensionality) { + case SGF_2D: + scene = desc->geometry.scn2d; + gebhart_radiative_path = gebhart_radiative_path_2d; + break; + case SGF_3D: + scene = desc->geometry.scn3d; + gebhart_radiative_path = gebhart_radiative_path_3d; + break; + default: FATAL("Unreachable code\n"); break; + } + /* Check scene active sessions */ - S3D(scene_get_session_mask(desc->scene, &mask)); - if(!(mask & S3D_TRACE) || !(mask & S3D_GET_PRIMITIVE)) { + SXD_FUNC(scene_get_session_mask(scene, &mask)); + if(!(mask & SXD_ENUM(TRACE)) || !(mask & SXD_ENUM(GET_PRIMITIVE))) { if(dev->verbose) { logger_print(dev->logger, LOG_ERROR, - "No active trace/get_primitive session on the Star-3D scene\n"); + "No active trace/get_primitive session on the Star-%dD scene\n", + desc->dimensionality == SGF_2D ? 2 : 3); } res = RES_BAD_ARG; goto error; } /* Check submitted primitive_id */ - S3D(scene_primitives_count(desc->scene, &nprims)); + SXD_FUNC(scene_primitives_count(scene, &nprims)); if(primitive_id >= nprims) { if(dev->verbose) { logger_print(dev->logger, LOG_ERROR, @@ -375,7 +252,7 @@ sgf_integrate } /* Compute an epsilon relatively to the scene size */ - S3D(scene_get_aabb(desc->scene, lower, upper)); + SXD_FUNC(scene_get_aabb(scene, lower, upper)); epsilon = upper[0] - lower[0]; epsilon = MMIN(epsilon, upper[1] - lower[1]); epsilon = MMIN(epsilon, upper[2] - lower[2]); @@ -439,6 +316,9 @@ exit: error: if(estimator) SGF(estimator_ref_put(estimator)); goto exit; + + #undef SXD_FUNC + #undef SXD_ENUM } res_T diff --git a/src/sgf_realisation.h b/src/sgf_realisation.h @@ -0,0 +1,340 @@ +/* Copyright (C) 2015-2016 EDF S.A., France (syrthes-support@edf.fr) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* + * Generate the gebhart_radiative_path_<2|3>d realisation functions with + * respect to the SGF_DIMENSIONALITY macro + */ + +#ifndef SGF_DIMENSIONALITY + +#ifndef SGF_REALISATION_H +#define SGF_REALISATION_H + +#include <star/s2d.h> +#include <star/s3d.h> +#include <star/ssp.h> +#include <rsys/float2.h> + +/* How many self intersections are tested on the same ray before an error + * occurs */ +#define NSELF_HITS_MAX 10 + +struct accum { + double radiative_flux; + double sqr_radiative_flux; +}; + +/* Type of the realisation function. Return RES_BAD_OP on numerical issue. i.e. + * the radiative path is skipped */ +typedef res_T +(*gebhart_radiative_path_T) + (struct sgf_device* dev, + struct accum* accums, + struct accum* accums_infinity, + struct ssp_rng* rng, + const float epsilon, + const size_t primitive_id, + struct sgf_scene_desc* desc); + +static FINLINE void +accum_weight(struct accum* accums, const size_t iface, const double weight) +{ + ASSERT(accums); + accums[iface].radiative_flux += weight; + accums[iface].sqr_radiative_flux += weight * weight; +} + +/******************************************************************************* + * 2D helper functions + ******************************************************************************/ +static FINLINE void +primitive2d_get_normal(struct s2d_primitive* prim, float normal[3]) +{ + struct s2d_attrib attr; + const float s = 0; + ASSERT(prim && normal); + S2D(primitive_get_attrib(prim, S2D_GEOMETRY_NORMAL, s, &attr)); + ASSERT(attr.type == S2D_FLOAT2); + f2_normalize(normal, attr.value); + normal[2] = 0.f; +} + +static FINLINE void +primitive2d_sample_position + (struct s2d_primitive* prim, + struct ssp_rng* rng, + float position[3]) +{ + struct s2d_attrib attr; + float s; + ASSERT(prim && position); + S2D(primitive_sample(prim, ssp_rng_canonical_float(rng), &s)); + S2D(primitive_get_attrib(prim, S2D_POSITION, s, &attr)); + ASSERT(attr.type == S2D_FLOAT2); + f2_set(position, attr.value); + position[2] = 0.f; +} + +static FINLINE void +hit2d_get_normal(struct s2d_hit* hit, float normal[3]) +{ + ASSERT(hit && normal); + f2_normalize(normal, hit->normal); + normal[2] = 0.f; +} + +/******************************************************************************* + * 3D helper functions + ******************************************************************************/ +static FINLINE void +primitive3d_get_normal(struct s3d_primitive* prim, float normal[3]) +{ + struct s3d_attrib attr; + const float st[2] = { 0.f, 0.f }; + ASSERT(prim && normal); + S3D(primitive_get_attrib(prim, S3D_GEOMETRY_NORMAL, st, &attr)); + ASSERT(attr.type == S3D_FLOAT3); + f3_normalize(normal, attr.value); +} + +static FINLINE void +primitive3d_sample_position + (struct s3d_primitive* prim, + struct ssp_rng* rng, + float position[3]) +{ + struct s3d_attrib attr; + float st[2]; + float u, v; + ASSERT(prim && position); + + u = ssp_rng_canonical_float(rng); + v = ssp_rng_canonical_float(rng); + S3D(primitive_sample(prim, u, v, st)); + S3D(primitive_get_attrib(prim, S3D_POSITION, st, &attr)); + ASSERT(attr.type == S3D_FLOAT3); + f3_set(position, attr.value); +} + +static FINLINE void +hit3d_get_normal(struct s3d_hit* hit, float normal[3]) +{ + ASSERT(hit && normal); + f3_normalize(normal, hit->normal); +} + +#endif /* SGF_REALISATION_H */ + +#else /* !SGF_DIMENSIONALITY */ + +#if SGF_DIMENSIONALITY == 2 + #define GEBHART_RADIATIVE_PATH gebhart_radiative_path_2d + + /* Wrap type */ + #define sXd_scene_T struct s2d_scene + #define sXd_hit_T struct s2d_hit + #define sXd_primitive_T struct s2d_primitive + + /* Wrap macros */ + #define SXD_HIT_NONE(Hit) S2D_HIT_NONE(Hit) + #define SXD_PRIMITIVE_EQ(A, B) S2D_PRIMITIVE_EQ(A, B) + + /* Wrap Functions */ + #define sXd_scene_get_primitive(Scn, IPrim, prim) \ + S2D(scene_get_primitive(Scn, IPrim, prim)) + #define sXd_primitive_get_normal(Prim, Normal) \ + primitive2d_get_normal(Prim, Normal) + #define sXd_primitive_sample_position(Prim, Rng, Position) \ + primitive2d_sample_position(Prim, Rng, Position) + #define sXd_scene_trace_ray(Scn, Org, Dir, Range, Hit) \ + S2D(scene_trace_ray_3d(Scn, Org, Dir, Range, NULL, Hit)) + #define sXd_hit_get_normal(Hit, Normal) \ + hit2d_get_normal(Hit, Normal) + #define sgf_scene_desc_get_sXd_scene(Desc) (Desc)->geometry.scn2d + +#elif SGF_DIMENSIONALITY == 3 + #define GEBHART_RADIATIVE_PATH gebhart_radiative_path_3d + + /* Wrap type */ + #define sXd_scene_T struct s3d_scene + #define sXd_hit_T struct s3d_hit + #define sXd_primitive_T struct s3d_primitive + + /* Wrap macros */ + #define SXD_HIT_NONE(Hit) S3D_HIT_NONE(Hit) + #define SXD_PRIMITIVE_EQ(A, B) S3D_PRIMITIVE_EQ(A, B) + + /* Wrap Functions */ + #define sXd_scene_get_primitive(Scn, IPrim, prim) \ + S3D(scene_get_primitive(Scn, IPrim, prim)) + #define sXd_primitive_get_normal(Prim, Normal) \ + primitive3d_get_normal(Prim, Normal) + #define sXd_primitive_sample_position(Prim, Rng, Position) \ + primitive3d_sample_position(Prim, Rng, Position) + #define sXd_scene_trace_ray(Scn, Org, Dir, Range, Hit) \ + S3D(scene_trace_ray(Scn, Org, Dir, Range, NULL, Hit)) + #define sXd_hit_get_normal(Hit, Normal) \ + hit3d_get_normal(Hit, Normal) + #define sgf_scene_desc_get_sXd_scene(Desc) (Desc)->geometry.scn3d +#else + #error Unexpected dimensionility +#endif + +static res_T +GEBHART_RADIATIVE_PATH + (struct sgf_device* dev, + struct accum* accums, + struct accum* accums_infinity, + struct ssp_rng* rng, + const float epsilon, + const size_t primitive_id, + struct sgf_scene_desc* desc) +{ + sXd_scene_T* scene; + sXd_hit_T hit; + sXd_primitive_T prim_from, prim; + size_t nself_hits = 0; + const double trans_min = 1.e-8; /* Minimum transmissivity threshold */ + double proba_reflec_spec; + double transmissivity, emissivity, reflectivity, specularity; +#ifndef NDEBUG + double infinite_radiative_flux = 0.0; + double sum_radiative_flux = 0.f; +#endif + float vec0[3]; /* Temporary vector */ + float normal[3]; /* Geometric normal */ + float pos[3]; /* Radiative path position */ + float dir[4]; /* Radiative path direction. dir[3] <=> sampled dir pdf */ + float range[2]; /* Traced ray range */ + ASSERT(accums && accums_infinity && epsilon > 0.f && rng && desc); + + scene = sgf_scene_desc_get_sXd_scene(desc); + + /* Discard faces with no emissivity */ + emissivity = desc->get_material_property(desc->material, + SGF_MATERIAL_EMISSIVITY, primitive_id, 0); + if(emissivity <= 0.0) + return RES_OK; + + /* Retrieve the scene primitive */ + sXd_scene_get_primitive(scene, (unsigned)primitive_id, &prim); + /* Get the geometric normal of the input primitive */ + sXd_primitive_get_normal(&prim, normal); + /* Uniformly sample prim to define the origin of the radiative path */ + sXd_primitive_sample_position(&prim, rng, pos); + /* Cosine weighted sampling of the radiative path direction around `normal' */ + ssp_ran_hemisphere_cos(rng, normal, dir); + + transmissivity = 1.0; + for(;;) { /* Here we go */ + prim_from = prim; + range[0] = epsilon, range[1] = FLT_MAX; + + nself_hits = 0; + do { /* Handle auto intersection */ + sXd_scene_trace_ray(scene, pos, dir, range, &hit); + if(!SXD_HIT_NONE(&hit)) range[0] = MMAX(range[0], hit.distance); + range[0] = nextafterf(range[0], range[1]); + } while(SXD_PRIMITIVE_EQ(&hit.prim, &prim_from) /* Self hit */ + && ++nself_hits < NSELF_HITS_MAX); /* Too many self hits */ + + if(nself_hits >= NSELF_HITS_MAX) { + if(dev->verbose) { + logger_print(dev->logger, LOG_ERROR, + "Too many self hit on a given ray. Ray origin: %g, %g, %g\n", + SPLIT3(pos)); + } + return RES_BAD_OP; + } + + if(SXD_HIT_NONE(&hit)) { /* The ray is outside the volume */ + accum_weight(accums_infinity, prim.scene_prim_id, transmissivity); +#ifndef NDEBUG + infinite_radiative_flux = transmissivity; +#endif + break; + } + + /* Retrieve the hit position and the hit primitive id */ + f3_mulf(vec0, dir, hit.distance); + f3_add(pos, vec0, pos); + prim = hit.prim; + + /* Fetch material property */ + emissivity = desc->get_material_property(desc->material, + SGF_MATERIAL_EMISSIVITY, prim.scene_prim_id, 0); + specularity = desc->get_material_property(desc->material, + SGF_MATERIAL_SPECULARITY, prim.scene_prim_id, 0); + reflectivity = desc->get_material_property(desc->material, + SGF_MATERIAL_REFLECTIVITY, prim.scene_prim_id, 0); + + if(transmissivity > trans_min) { + const double weight = transmissivity * emissivity; + accum_weight(accums, prim.scene_prim_id, weight); + transmissivity = transmissivity * (1.0 - emissivity); +#ifndef NDEBUG + sum_radiative_flux += weight; +#endif + } else { + /* Russian roulette */ + if(ssp_rng_canonical(rng) < emissivity) { + const double weight = transmissivity; + accum_weight(accums, prim.scene_prim_id, weight); +#ifndef NDEBUG + sum_radiative_flux += weight; +#endif + break; + } + } + + if(reflectivity <= 0.0) break; + + proba_reflec_spec = specularity / reflectivity; + sXd_hit_get_normal(&hit, normal); + + if(ssp_rng_canonical(rng) >= proba_reflec_spec) { /* Diffuse reflection */ + ssp_ran_hemisphere_cos(rng, normal, dir); + ASSERT(f3_dot(normal, dir) > 0); + } else { /* Specular reflection */ + const float tmp = -2.f * f3_dot(dir, normal); + f3_mulf(vec0, normal, tmp); + f3_add(dir, dir, vec0); + f3_normalize(dir, dir); + } + } +#if !defined(NDEBUG) + /* Check the energy conservation property */ + ASSERT(eq_eps(sum_radiative_flux + infinite_radiative_flux, 1.0, 1.e6)); +#endif + return RES_OK; +} + +#undef GEBHART_RADIATIVE_PATH +#undef sXd_scene_T +#undef sXd_hit_T +#undef sXd_primitive_T +#undef SXD_HIT_NONE +#undef SXD_PRIMITIVE_EQ +#undef sXd_scene_get_primitive +#undef sXd_primitive_get_normal +#undef sXd_primitive_sample_position +#undef sXd_scene_trace_ray +#undef sXd_hit_get_normal +#undef sgf_scene_desc_get_sXd_scene + +#undef SGF_DIMENSIONALITY + +#endif /* !SGF_DIMENSIONALITY */ diff --git a/src/test_sgf_cube.c b/src/test_sgf_cube.c @@ -127,7 +127,8 @@ main(int argc, char** argv) scn_desc.get_material_property = get_material_property; scn_desc.material = &mtr; scn_desc.spectral_bands_count = 1; - scn_desc.scene = scn; + scn_desc.dimensionality = SGF_3D; + scn_desc.geometry.scn3d = scn; status = sa_add(status, nprims*nprims); rngs = sa_add(rngs, nbuckets); diff --git a/src/test_sgf_tetrahedron.c b/src/test_sgf_tetrahedron.c @@ -94,7 +94,8 @@ main(int argc, char** argv) desc.get_material_property = get_material_property; desc.material = &mtr; desc.spectral_bands_count = 1; - desc.scene = scn; + desc.dimensionality = SGF_3D; + desc.geometry.scn3d = scn; status = sa_add(status, nprims*nprims); rngs = sa_add(rngs, nbuckets);