commit c586130ec748d5f8c05072625d3bf6557f145511 parent 62045347e6ecb9a07dcae03418e93512910a4658 Author: Vincent Forest <vincent.forest@meso-star.com> Date: Wed, 4 Jan 2023 10:37:56 +0100 Merge branch 'release_0.8' Diffstat:
51 files changed, 723 insertions(+), 60 deletions(-)
diff --git a/README.md b/README.md @@ -13,11 +13,10 @@ Greenstein and Rayleigh phase functions, etc. The library uses [CMake](http://www.cmake.org) and the [RCMake](https://gitlab.com/vaplv/rcmake/#tab-readme) package to build. It also -depends on the -[Star-SP](https://gitlab.com/meso-star/star-sp/#tab-readme) and the -[RSys](https://gitlab.com/vaplv/rsys/#tab-readme) libraries. The -[RSIMD](https://gitlab.com/vaplv/rsimd) library is an optional dependency that, -if it is installed, is used to speed up the setup of the built-in RDG-FA phase +depends on the [Star-SP](https://gitlab.com/meso-star/star-sp/#tab-readme) and +the [RSys](https://gitlab.com/vaplv/rsys/#tab-readme) libraries. The +[RSIMD](https://gitlab.com/vaplv/rsimd) library is an optional dependency +which, if installed, is used to speed up the configuration of the RDG-FA phase function. First ensure that CMake is installed on your system. Then install the RCMake @@ -27,10 +26,15 @@ project from the `cmake/CMakeLists.txt` file by appending to the ## Release notes +### Version 0.8 + +Added support for discrete phase functions, i.e. phase functions whose values +are only defined for a set of angles. + ### Version 0.7.2 Sets the required version of Star-SampPling to 0.12. This version fixes -compilation errors with gcc 11 but introduce API breaks. +compilation errors with gcc 11 but introduces API breaks. ### Version 0.7.1 @@ -80,8 +84,8 @@ Add a built-in phase function using the ## License -Star-ScatteringFunctions is Copyright (C) 2016-2018, 2021 -|Meso|Star>(<contact@meso-star.com>). It is a free software released under the -GPL v3+ license. You are welcome to redistribute it under certain conditions; -refer to the COPYING file for details. +Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (<contact@meso-star.com>). +Star-ScatteringFunctions is free software released under the GPL v3+ license. +You are welcome to redistribute it under certain conditions; refer to the +COPYING file for details. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +# Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) # # 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 @@ -80,8 +80,8 @@ endif() # Define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 7) -set(VERSION_PATCH 2) +set(VERSION_MINOR 8) +set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SSF_FILES_DOC COPYING README.md) @@ -106,6 +106,7 @@ set(SSF_FILES_SRC ssf_microfacet_distribution.c ssf_microfacet_reflection.c ssf_phase.c + ssf_phase_discrete.c ssf_phase_hg.c ssf_phase_rayleigh.c ssf_phase_rdgfa.c @@ -164,6 +165,7 @@ if(NOT NO_TEST) new_test(test_ssf_microfacet_distribution) new_test(test_ssf_microfacet_reflection) new_test(test_ssf_phase) + new_test(test_ssf_phase_discrete) new_test(test_ssf_phase_hg) new_test(test_ssf_phase_rayleigh) new_test(test_ssf_specular_dielectric_dielectric_reflection) diff --git a/src/ssf.c b/src/ssf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf.h b/src/ssf.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 @@ -235,6 +235,27 @@ struct ssf_phase_rdgfa_interval { static const struct ssf_phase_rdgfa_interval SSF_PHASE_RDGFA_INTERVAL_NULL = SSF_PHASE_RDGFA_INTERVAL_NULL__; +struct ssf_discrete_item { + double theta; /* In radian */ + double value; +}; +#define SSF_DISCRETE_ITEM_NULL__ {0,0} +static const struct ssf_discrete_item SSF_DISCRETE_ITEM_NULL = + SSF_DISCRETE_ITEM_NULL__; + +/* Discrete phase function input arguments */ +struct ssf_discrete_setup_args { + void (*get_item) /* Returns a given discrete item */ + (const size_t iitem, + struct ssf_discrete_item* item, + void* context); + void* context; /* User defined data sent to `get_item' functor */ + size_t nitems; /* #discrete items */ +}; +#define SSF_DISCRETE_SETUP_ARGS_NULL__ {NULL,NULL,0} +static const struct ssf_discrete_setup_args +SSF_DISCRETE_SETUP_ARGS_NULL = SSF_DISCRETE_SETUP_ARGS_NULL__; + BEGIN_DECLS /******************************************************************************* @@ -368,6 +389,9 @@ SSF_API const struct ssf_phase_type ssf_phase_rayleigh; * Radiative Transfer Vol 133, pp. 374-381, 2014 */ SSF_API const struct ssf_phase_type ssf_phase_rdgfa; +/* Discrete phase function normalized to 1 */ +SSF_API const struct ssf_phase_type ssf_phase_discrete; + /******************************************************************************* * SSF API ******************************************************************************/ @@ -508,6 +532,11 @@ ssf_phase_hg_setup (struct ssf_phase* phase, const double g); /* Asymmetric parameter in [-1, 1] */ +SSF_API res_T +ssf_phase_discrete_setup + (struct ssf_phase* phase, + const struct ssf_discrete_setup_args* args); + /******************************************************************************* * RDGFA phase function ******************************************************************************/ diff --git a/src/ssf_beckmann_distribution.c b/src/ssf_beckmann_distribution.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_blinn_distribution.c b/src/ssf_blinn_distribution.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_bsdf.c b/src/ssf_bsdf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_bsdf_c.h b/src/ssf_bsdf_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_fresnel.c b/src/ssf_fresnel.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_fresnel_c.h b/src/ssf_fresnel_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_fresnel_constant.c b/src/ssf_fresnel_constant.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_fresnel_dielectric_conductor.c b/src/ssf_fresnel_dielectric_conductor.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_fresnel_dielectric_dielectric.c b/src/ssf_fresnel_dielectric_dielectric.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_fresnel_no_op.c b/src/ssf_fresnel_no_op.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_lambertian_reflection.c b/src/ssf_lambertian_reflection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_microfacet_distribution.c b/src/ssf_microfacet_distribution.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_microfacet_distribution_c.h b/src/ssf_microfacet_distribution_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_microfacet_reflection.c b/src/ssf_microfacet_reflection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_optics.h b/src/ssf_optics.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_phase.c b/src/ssf_phase.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_phase_c.h b/src/ssf_phase_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_phase_discrete.c b/src/ssf_phase_discrete.c @@ -0,0 +1,377 @@ +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) + * + * 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/>. */ + +#define _POSIX_C_SOURCE 200112L /* nextafter */ + +#include "ssf.h" +#include "ssf_phase_c.h" + +#include <star/ssp.h> + +#include <rsys/algorithm.h> +#include <rsys/double3.h> +#include <rsys/dynamic_array_double.h> + +#include <math.h> /* nextafter */ + +struct discrete_item { + double cos_theta; + double value; +}; + +/* Define the dynamic array of discrete items */ +#define DARRAY_NAME discrete_item +#define DARRAY_DATA struct discrete_item +#include <rsys/dynamic_array.h> + +struct discrete { + struct darray_discrete_item items; + struct darray_double cumulative; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE res_T +check_ssf_discrete_setup_args + (const struct ssf_discrete_setup_args* args) +{ + struct ssf_discrete_item item; + if(!args || args->nitems < 2 || !args->get_item) + return RES_BAD_ARG; + + /* Invalid 1st angle */ + args->get_item(0, &item, args->context); + if(item.theta != 0) + return RES_BAD_ARG; + + /* Invalid last angle */ + args->get_item(args->nitems-1, &item, args->context); + if(!eq_eps(item.theta, PI, 1.e-4)) + return RES_BAD_ARG; + + return RES_OK; +} + +static res_T +setup_discrete_items + (struct discrete* discrete, + const struct ssf_discrete_setup_args* args) +{ + struct discrete_item* items = NULL; + size_t i = 0; + res_T res = RES_OK; + ASSERT(discrete && args); + + res = darray_discrete_item_resize(&discrete->items, args->nitems); + if(res != RES_OK) goto error; + items = darray_discrete_item_data_get(&discrete->items); + + FOR_EACH(i, 0, args->nitems) { + struct ssf_discrete_item item; + args->get_item(i, &item, args->context); + items[i].cos_theta = cos(item.theta); + items[i].value = item.value; + + /* Angles must be sorted in ascending order */ + if(i > 0 && items[i].cos_theta > items[i-1].cos_theta) { + res = RES_BAD_ARG; + goto error; + } + } + +exit: + return res; +error: + goto exit; +} + +static res_T +compute_cumulative(struct discrete* discrete) +{ + struct discrete_item* items = NULL; + double* cumul = NULL; + double alpha = 0; + size_t n; + size_t i; + res_T res = RES_OK; + ASSERT(discrete); + + n = darray_discrete_item_size_get(&discrete->items); + ASSERT(n >= 2); + + res = darray_double_resize(&discrete->cumulative, n); + if(res != RES_OK) goto error; + + items = darray_discrete_item_data_get(&discrete->items); + cumul = darray_double_data_get(&discrete->cumulative); + + alpha = 0; + cumul[0] = alpha; + FOR_EACH(i, 1, n) { + alpha += PI + * (items[i-1].value + items[i].value) + * (items[i-1].cos_theta - items[i].cos_theta); + + cumul[i] = alpha; + } + + /* Ensure that the phase function is normalized */ + if(!eq_eps(alpha, 1, 1.e-6)) { + const double rcp_alpha = 1.0 / alpha; + FOR_EACH(i, 0, n) { + items[i].value *= rcp_alpha; + cumul[i] *= rcp_alpha; + } + } + +exit: + return res; +error: + goto exit; +} + +static INLINE int +cmp_discrete_item(const void* a, const void* b) +{ + const struct discrete_item* item; + double key; + ASSERT(a && b); + + key = *((double*)a); + item = b; + + /* The array is sorted in ascending order of the angles while it stores the + * cosine of the angles. In the following, we reverse the test to find the + * first entry whose _angle_ is not less than the angle whose cosine is the + * key sought. This ensures that the array is sorted in ascending order as + * expected by the search_lower_bound routine */ + if(key < item->cos_theta) { + return +1; + } else if(key > item->cos_theta) { + return -1; + } else { + return 0; + } +} + +static INLINE int +cmp_cumul(const void* a, const void* b) +{ + double key; + double cumul; + ASSERT(a && b); + + key = *((double*)a); + cumul = *((double*)b); + + if(key < cumul) { + return -1; + } else if(key > cumul) { + return +1; + } else { + return 0; + } +} + +/******************************************************************************* + * Private functions + ******************************************************************************/ +static res_T +discrete_init(struct mem_allocator* allocator, void* phase) +{ + struct discrete* discrete = phase; + ASSERT(phase); + darray_discrete_item_init(allocator, &discrete->items); + darray_double_init(allocator, &discrete->cumulative); + return RES_OK; +} + +static void +discrete_release(void* phase) +{ + struct discrete* discrete = phase; + ASSERT(phase); + darray_discrete_item_release(&discrete->items); + darray_double_release(&discrete->cumulative); +} + +static double +discrete_eval(void* phase, const double wo[3], const double wi[3]) +{ + const struct discrete* discrete = phase; + const struct discrete_item* items = NULL; + double v[3]; + double cos_theta = 0; + double value = 0; + size_t nitems = 0; + ASSERT(phase && wo && wi); + ASSERT(d3_is_normalized(wo) && d3_is_normalized(wi)); + + d3_minus(v, wo); + cos_theta = d3_dot(v, wi); + + items = darray_discrete_item_cdata_get(&discrete->items); + nitems = darray_discrete_item_size_get(&discrete->items); + ASSERT(nitems >= 2); + + if(eq_eps(cos_theta, 1, 1.e-6)) { + value = items[0].value; + } else if(eq_eps(cos_theta, 0, 1.e-6)) { + value = items[nitems-1].value; + } else { + const struct discrete_item* found_item = NULL; + size_t iitem = 0; + double u = 0; + + /* Search for the discrete item whose angle is greater or equal to the + * angle between wo and wi */ + found_item = search_lower_bound + (&cos_theta, items, nitems, sizeof(*items), cmp_discrete_item); + iitem = (size_t)(found_item - items); + ASSERT(found_item && iitem > 0 && iitem < nitems); + ASSERT(cos_theta < items[iitem-1].cos_theta); + ASSERT(cos_theta >= items[iitem].cos_theta); + + /* Linearly interpolate the phase function value */ + u = + (cos_theta - items[iitem].cos_theta) + / (items[iitem-1].cos_theta - items[iitem].cos_theta); + value = items[iitem].value + u*(items[iitem-1].value - items[iitem].value); + } + + return value; +} + +static void +discrete_sample + (void* phase, + struct ssp_rng* rng, + const double wo[3], + double wi[3], + double *pdf) +{ + const struct discrete* discrete = phase; + const struct discrete_item* items = NULL; + const double* cumul = NULL; + double frame[9]; + double w[3]; + double cos_theta = 0; + double sin_theta = 0; + double phi = 0; + size_t ncumul = 0; + size_t i = 0; + double r = 0; + ASSERT(phase && rng && wo && wi); + ASSERT(d3_is_normalized(wo)); + + items = darray_discrete_item_cdata_get(&discrete->items); + cumul = darray_double_cdata_get(&discrete->cumulative); + ncumul = darray_double_size_get(&discrete->cumulative); + ASSERT(ncumul == darray_discrete_item_size_get(&discrete->items)); + + /* Sample r in [0, 1] */ + r = ssp_rng_uniform_double(rng, 0, nextafter(1, 2)); + if(r == 0) { + cos_theta = 1; + sin_theta = 0; + } else if(r ==1) { + cos_theta =-1; + sin_theta = 0; + } else { + double* found_cumul = NULL; + double u; + + /* Search for the first entry in the cumulative that is greater than or equal + * to the sampled number in [0, 1] */ + found_cumul = search_lower_bound(&r, cumul, ncumul, sizeof(*cumul), cmp_cumul); + + i = (size_t)(found_cumul - cumul); + ASSERT(found_cumul && i > 0 && i < ncumul); + + /* Linearly interpolate the sampled cos_theta */ + u = (r - cumul[i-1]) / (cumul[i] - cumul[i-1]); + ASSERT(r >= cumul[i-1] && r < cumul[i]); + cos_theta = items[i-1].cos_theta + u*(items[i].cos_theta - items[i-1].cos_theta); + + /* Compute the sinus of theta from its cosine */ + sin_theta = sqrt(1 - cos_theta*cos_theta); + } + + /* Uniformly sample phi in [0, 2PI[ */ + phi = ssp_rng_uniform_double(rng, 0, 2*PI); + + /* Calculate the Cartesian coordinates of the direction in the local + * coordinate system of the phase function */ + wi[0] = cos(phi) * sin_theta; + wi[1] = sin(phi) * sin_theta; + wi[2] = cos_theta; + + /* Calculate the transformation matrix from the local coordinate system of + * the phase function to the absolute coordinate system. Note that by + * convention in Star-SF the directions point outward from the scattering + * position. That's why we reverse 'wo' to point inside the scattering + * position */ + d33_basis(frame, d3_minus(w, wo)); + d33_muld3(wi, frame, wi); + ASSERT(eq_eps(d3_dot(wi, w), cos_theta, fabs(cos_theta*1.e-6))); + + if(pdf) *pdf = discrete_eval(phase, wo, wi); +} + +/******************************************************************************* + * Exported symbols + ******************************************************************************/ +const struct ssf_phase_type ssf_phase_discrete = { + discrete_init, + discrete_release, + discrete_sample, + discrete_eval, + discrete_eval, + sizeof(struct discrete), + ALIGNOF(struct discrete) +}; + +res_T +ssf_phase_discrete_setup + (struct ssf_phase* phase, + const struct ssf_discrete_setup_args* args) +{ + struct discrete* discrete = NULL; + res_T res = RES_OK; + + if(!phase || !PHASE_TYPE_EQ(&phase->type, &ssf_phase_discrete)) { + res = RES_BAD_ARG; + goto error; + } + res = check_ssf_discrete_setup_args(args); + if(res != RES_OK) goto error; + + discrete = phase->data; + + res = setup_discrete_items(discrete, args); + if(res != RES_OK) goto error; + res = compute_cumulative(discrete); + if(res != RES_OK) goto error; + +exit: + return res; +error: + if(discrete) { + darray_discrete_item_purge(&discrete->items); + darray_double_purge(&discrete->cumulative); + } + goto exit; +} diff --git a/src/ssf_phase_hg.c b/src/ssf_phase_hg.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_phase_rayleigh.c b/src/ssf_phase_rayleigh.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_phase_rdgfa.c b/src/ssf_phase_rdgfa.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_phase_rdgfa_simdX.h b/src/ssf_phase_rdgfa_simdX.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_pillbox_distribution.c b/src/ssf_pillbox_distribution.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_specular_dielectric_dielectric_interface.c b/src/ssf_specular_dielectric_dielectric_interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_specular_reflection.c b/src/ssf_specular_reflection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/ssf_thin_specular_dielectric.c b/src/ssf_thin_specular_dielectric.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_beckmann_distribution.c b/src/test_ssf_beckmann_distribution.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_blinn_distribution.c b/src/test_ssf_blinn_distribution.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_bsdf.c b/src/test_ssf_bsdf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_fresnel.c b/src/test_ssf_fresnel.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_fresnel_constant.c b/src/test_ssf_fresnel_constant.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_fresnel_dielectric_conductor.c b/src/test_ssf_fresnel_dielectric_conductor.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_fresnel_dielectric_dielectric.c b/src/test_ssf_fresnel_dielectric_dielectric.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_fresnel_no_op.c b/src/test_ssf_fresnel_no_op.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_lambertian_reflection.c b/src/test_ssf_lambertian_reflection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_microfacet_distribution.c b/src/test_ssf_microfacet_distribution.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_microfacet_reflection.c b/src/test_ssf_microfacet_reflection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_phase.c b/src/test_ssf_phase.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_phase_discrete.c b/src/test_ssf_phase_discrete.c @@ -0,0 +1,251 @@ +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) + * + * 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/>. */ + +#include "ssf.h" +#include "test_ssf_utils.h" + +#include <star/ssp.h> + +/* Pre-normalized discrete phase function */ +static const struct ssf_discrete_item g_items[] = { + {0.0000000000000000, 68.603348420512177}, + {1.7453292519943295e-2, 50.735150232030207}, + {3.4906585039886591e-2, 24.908999683188814}, + {5.2359877559829883e-2, 11.936459532538100}, + {6.9813170079773182e-2, 6.5845291580807999}, + {8.7266462599716474e-2, 4.1115681738409000}, + {0.10471975511965977, 2.7971255874985190}, + {0.12217304763960307, 2.0200365258652173}, + {0.13962634015954636, 1.5241311066143122}, + {0.15707963267948966, 1.1891144970876193}, + {0.17453292519943295, 0.95259719392666198}, + {0.26179938779914941, 0.40729722471207858}, + {0.34906585039886590, 0.22439173045102234}, + {0.52359877559829882, 9.8911118269091880e-2}, + {0.69813170079773179, 5.7013070175769544e-2}, + {0.87266462599716477, 3.8428151500227763e-2}, + {1.0471975511965976, 2.8819207088581208e-2}, + {1.2217304763960306, 2.3435147759515759e-2}, + {1.3962634015954636, 2.0323680045268303e-2}, + {1.5707963267948966, 1.8554414090107985e-2}, + {1.7453292519943295, 1.7631650380735577e-2}, + {1.9198621771937625, 1.7273221501888446e-2}, + {2.0943951023931953, 1.7265595355529996e-2}, + {2.2689280275926285, 1.7471501307208134e-2}, + {2.4434609527920612, 1.7768921015187674e-2}, + {2.6179938779914944, 1.8089219162242556e-2}, + {2.7925268031909272, 1.8356134284788293e-2}, + {2.8797932657906435, 1.8455274187448138e-2}, + {2.9670597283903604, 1.8531535651032636e-2}, + {2.9845130209103035, 1.8546787943749535e-2}, + {3.0019663134302466, 1.8554414090107985e-2}, + {3.0194196059501901, 1.8562040236466435e-2}, + {3.0368728984701332, 1.8569666382824885e-2}, + {3.0543261909900763, 1.8584918675541785e-2}, + {3.0717794835100198, 1.8638301700050933e-2}, + {3.0892327760299634, 1.9156879652425504e-2}, + {3.1066860685499069, 2.1879413902392029e-2}, + {3.1241393610698500, 2.8323507575281980e-2}, + {3.1415926535897931, 3.2632280267806027e-2} +}; +static const size_t g_nitems = sizeof(g_items) / sizeof(*g_items); + +struct context { + const struct ssf_discrete_item* items; + size_t nitems; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +get_item(const size_t id, struct ssf_discrete_item* item, void* context) +{ + const struct context* ctx = context; + CHK(id < ctx->nitems); + *item = ctx->items[id]; +} + +static void +test_setup(struct ssf_phase* discrete) +{ + struct ssf_discrete_setup_args args = SSF_DISCRETE_SETUP_ARGS_NULL; + struct context ctx; + struct ssf_phase* dummy = NULL; + ASSERT(discrete); + + ctx.items = g_items; + ctx.nitems = g_nitems; + + args.get_item = get_item; + args.context = &ctx; + args.nitems = g_nitems; + + CHK(ssf_phase_discrete_setup(NULL, &args) == RES_BAD_ARG); + CHK(ssf_phase_discrete_setup(discrete, NULL) == RES_BAD_ARG); + CHK(ssf_phase_discrete_setup(discrete, &args) == RES_OK); + + /* Invalid phase function type */ + CHK(ssf_phase_create(&mem_default_allocator, &phase_dummy, &dummy) == RES_OK); + CHK(ssf_phase_discrete_setup(dummy, &args) == RES_BAD_ARG); + CHK(ssf_phase_ref_put(dummy) == RES_OK); + + /* Invalid last #items */ + ctx.nitems = args.nitems = 1; + CHK(ssf_phase_discrete_setup(discrete, &args) == RES_BAD_ARG); + + /* Last angle is not PI */ + ctx.nitems = args.nitems = g_nitems - 1; + CHK(ssf_phase_discrete_setup(discrete, &args) == RES_BAD_ARG); + + /* First angle is not 0 */ + ctx.items = g_items + 1; + CHK(ssf_phase_discrete_setup(discrete, &args) == RES_BAD_ARG); +} + +static void +test_eval(struct ssf_phase* discrete, struct ssp_rng* rng) +{ + struct ssf_discrete_setup_args args = SSF_DISCRETE_SETUP_ARGS_NULL; + struct context ctx; + double wo[3]; + double wi[3]; + size_t i; + + ctx.items = g_items; + ctx.nitems = g_nitems; + args.get_item = get_item; + args.context = &ctx; + args.nitems = g_nitems; + CHK(ssf_phase_discrete_setup(discrete, &args) == RES_OK); + + d3(wo, 0, 0,-1); + d3(wi, 0, 0, 1); + CHK(ssf_phase_eval(discrete, wo, wi) == g_items[0].value); + d3(wo, 0, 0, 1); + CHK(ssf_phase_eval(discrete, wo, wi) == g_items[g_nitems-1].value); + + FOR_EACH(i, 0, 10) { + double cos_theta; + double val; + double ref; + size_t iitem; + + ssp_ran_sphere_uniform(rng, wo, NULL); + ssp_ran_sphere_uniform(rng, wi, NULL); + val = ssf_phase_eval(discrete, wo, wi); + + cos_theta = d3_dot(d3_minus(wo, wo), wi); + if(cos_theta == 0) { + ref = g_items[0].value; + } else { + const double theta = acos(cos_theta); + double u; + /* Look for the phase function discrete items to consider regarding the + * sampled wo and wi directions */ + FOR_EACH(iitem, 0, g_nitems) { + if(g_items[iitem].theta >= theta) break; + } + ASSERT(iitem < g_nitems && iitem > 0); + + /* Compute the parameter of the linear interpolation */ + u = cos_theta - cos(g_items[iitem-1].theta); + u /= cos(g_items[iitem].theta) - cos(g_items[iitem-1].theta); + + ref = g_items[iitem-1].value + u*(g_items[iitem].value - g_items[iitem-1].value); + } + CHK(eq_eps(val, ref, 1.e-6)); + } +} + +static void +test_sample(struct ssf_phase* discrete, struct ssp_rng* rng) +{ + struct ssf_discrete_setup_args args = SSF_DISCRETE_SETUP_ARGS_NULL; + struct context ctx; + double ref = 0; + size_t iitem; + size_t i; + + ctx.items = g_items; + ctx.nitems = g_nitems; + args.get_item = get_item; + args.context = &ctx; + args.nitems = g_nitems; + CHK(ssf_phase_discrete_setup(discrete, &args) == RES_OK); + + FOR_EACH(iitem, 1, g_nitems) { + const double mu0 = cos(g_items[iitem-1].theta); + const double mu1 = cos(g_items[iitem-0].theta); + const double phi0 = g_items[iitem-1].value; + const double phi1 = g_items[iitem-0].value; + const double delta_mu = mu0 - mu1; + ref += (mu0*phi0 + mu1*phi1)/2 * delta_mu; + } + ref *= 2*PI; + + FOR_EACH(i, 0, 10) { + const size_t N = 10000; + double wo[3]; + double wi[3]; + double sum = 0; + double sum2 = 0; + double E = 0; + double V = 0; + double SE = 0; + size_t ireal; + ssp_ran_sphere_uniform(rng, wo, NULL); + + FOR_EACH(ireal, 0, N) { + double w[3]; + double mu; + double pdf; + ssf_phase_sample(discrete, rng, wo, wi, &pdf); + + mu = d3_dot(d3_minus(w, wo), wi); + CHK(pdf == ssf_phase_eval(discrete, wo, wi)); + + sum += mu; + sum2 += mu*mu; + } + E = sum / (double)N; + V = sum2 / (double)N - E*E; + SE = sqrt(V/(double)N); + CHK(eq_eps(E, ref, 3*SE)); + } +} + +/******************************************************************************* + * Main function + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct ssp_rng* rng = NULL; + struct ssf_phase* discrete = NULL; + (void)argc, (void)argv; + + CHK(ssf_phase_create + (&mem_default_allocator, &ssf_phase_discrete, &discrete) == RES_OK); + CHK(ssp_rng_create(&mem_default_allocator, SSP_RNG_MT19937_64, &rng) == RES_OK); + + test_setup(discrete); + test_eval(discrete, rng); + test_sample(discrete, rng); + + CHK(ssf_phase_ref_put(discrete) == RES_OK); + CHK(ssp_rng_ref_put(rng) == RES_OK); + return 0; +} diff --git a/src/test_ssf_phase_hg.c b/src/test_ssf_phase_hg.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_phase_rayleigh.c b/src/test_ssf_phase_rayleigh.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_phase_rdgfa.c b/src/test_ssf_phase_rdgfa.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_pillbox_distribution.c b/src/test_ssf_pillbox_distribution.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_specular_dielectric_dielectric_reflection.c b/src/test_ssf_specular_dielectric_dielectric_reflection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_specular_reflection.c b/src/test_ssf_specular_reflection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_thin_specular_dielectric.c b/src/test_ssf_thin_specular_dielectric.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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 diff --git a/src/test_ssf_utils.h b/src/test_ssf_utils.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2018, 2021-2023 |Méso|Star> (contact@meso-star.com) * * 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