star-sf

Set of surface and volume scattering functions
git clone git://git.meso-star.fr/star-sf.git
Log | Files | Refs | README | LICENSE

commit f57ae592db312e611e55a5dce034764887a66f36
parent 863ed1ee241b1aae6870a7d30cc721f7341e6ef4
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 23 Sep 2016 10:30:02 +0200

Major update of the BSDF sample function and remove the BSDF view

Remove the BSDF view data structure since they are quite useless; the
BSDF view pre-computations can be silently performed directly into the
BSDF.

Add the rho function to the B<x|S>DF API. This functions is defined as:
  rho(wi) = \int_{2\Pi} B<x|S>DF(wo, wi) |wi.N| dwi

The BSDF components are now sampled wrt to Wi*rho_i(wo)/rho(wo) where Wi
is the weight of the i^th component, rho_i(wo) its rho value and rho(wo)
the rho value of the BSDF. The PDF of the sampled direction becomes:
  P(wi, wo) = \sum_{i=1..N} Wi*rho_i(wo)/rho(wo) * Pi(wo, wi)
with Pi(wo, wi) the probability to sample the direction wi wrt to wo for
the i^th component. Finally the BSDF reflectivity along wi is defined
as:
  R(wi, wo) = \sum_{i=1..N} Wi*BxDF(wi, wo)

Diffstat:
Mcmake/CMakeLists.txt | 4----
Msrc/ssf.h | 37+++++++++++--------------------------
Msrc/ssf_bsdf.c | 181++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/ssf_bsdf_c.h | 17-----------------
Dsrc/ssf_bsdf_view.c | 237-------------------------------------------------------------------------------
Dsrc/ssf_bsdf_view_c.h | 38--------------------------------------
Msrc/test_ssf_bsdf.c | 59++++++++++++++++++++++++++++++++++++++++++++---------------
Dsrc/test_ssf_bsdf_view.c | 111-------------------------------------------------------------------------------
8 files changed, 220 insertions(+), 464 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -46,8 +46,6 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SSF_FILES_DOC COPYING README.md) set(SSF_FILES_INC_API ssf.h) set(SSF_FILES_INC - ssf_bsdf_c.h - ssf_bsdf_view_c.h ssf_bxdf_c.h ssf_fresnel_c.h ssf_microfacet_distribution_c.h @@ -55,7 +53,6 @@ set(SSF_FILES_INC set(SSF_FILES_SRC ssf_beckmann_distribution.c ssf_bsdf.c - ssf_bsdf_view.c ssf_bxdf.c ssf_fresnel.c ssf_fresnel_dielectric_conductor.c @@ -100,7 +97,6 @@ if(NOT NO_TEST) endfunction() new_test(test_ssf_bsdf) - new_test(test_ssf_bsdf_view) new_test(test_ssf_bxdf) new_test(test_ssf_fresnel) new_test(test_ssf_fresnel_dielectric_conductor) diff --git a/src/ssf.h b/src/ssf.h @@ -166,7 +166,9 @@ SSF_API const struct ssf_microfacet_distribution_type ssf_beckmann_distribution; /******************************************************************************* * BSDF API - Bidirectionnal Scattering Distribution Function. Describes the - * way the light is scattered by a surface with a combination of BxDFs. + * way the light is scattered by a surface with a combination of BxDFs. Note + * that by convention the outgoing direction `wo' and the incoming direction + * `wi' point outward the surface. ******************************************************************************/ SSF_API res_T ssf_bsdf_create @@ -191,26 +193,9 @@ SSF_API res_T ssf_bsdf_clear (struct ssf_bsdf* bsdf); -/******************************************************************************* - * BSDF view API - State of the BSDF. Note that by convention the outgoing - * direction `wo' and the incoming direction `wi' point outward the surface. - ******************************************************************************/ -SSF_API res_T -ssf_bsdf_view_create - (struct ssf_bsdf* bsdf, - struct ssf_bsdf_view** view); - -SSF_API res_T -ssf_bsdf_view_ref_get - (struct ssf_bsdf_view* view); - -SSF_API res_T -ssf_bsdf_view_ref_put - (struct ssf_bsdf_view* view); - SSF_API double /* Value of the BSDF wrt to wo and the sampled wi */ -ssf_bsdf_view_sample - (struct ssf_bsdf_view* view, +ssf_bsdf_sample + (struct ssf_bsdf* bsdf, const double u, /* Canonical number */ const double v, /* Canonical number */ const double wo[3], /* Normalized outgoing direction */ @@ -219,22 +204,22 @@ ssf_bsdf_view_sample double* pdf); /* PDF to sample wi wrt wo */ SSF_API double /* Reflectivity */ -ssf_bsdf_view_eval - (struct ssf_bsdf_view* view, +ssf_bsdf_eval + (struct ssf_bsdf* bsdf, const double wo[3], /* Normalized outgoing direction */ const double N[3], /* Normalized surface normal */ const double wi[3]); /* Normalized incoming direction */ SSF_API double -ssf_bsdf_view_pdf - (struct ssf_bsdf_view* view, +ssf_bsdf_pdf + (struct ssf_bsdf* bsdf, const double wo[3], /* Normalized incoming direction */ const double N[3], /* Normalized surface normal */ const double wi[3]); /* Normalized outgoing direction */ SSF_API double /* \int_{2\pi} BSDF(wi, wo) |wi.N| dwi */ -ssf_bsdf_view_rho - (struct ssf_bsdf_view* view, +ssf_bsdf_rho + (struct ssf_bsdf* bsdf, const double wo[3]); /******************************************************************************* diff --git a/src/ssf_bsdf.c b/src/ssf_bsdf.c @@ -13,29 +13,51 @@ * 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_bsdf_c.h" -#include "ssf_bsdf_view_c.h" #include "ssf_bxdf_c.h" #include <rsys/double3.h> #include <rsys/double4.h> #include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +#define MAX_BxDFs 8 + +struct ssf_bsdf { + struct ssf_bxdf* bxdfs[MAX_BxDFs]; + + double uweights[MAX_BxDFs]; /* Unormalized component weights */ + double weights[MAX_BxDFs]; /* Normalized weights */ + int outdated_weights; /* Define if the normalized weights are outdated */ + + size_t nbxdfs; + + ref_T ref; + struct mem_allocator* allocator; +}; /******************************************************************************* * Helper functions ******************************************************************************/ +static INLINE void +bsdf_normalize_weights(struct ssf_bsdf* bsdf) +{ + double weights_sum = 0; + size_t i; + ASSERT(bsdf); + if(!bsdf->outdated_weights) return; + FOR_EACH(i, 0, bsdf->nbxdfs) weights_sum += bsdf->uweights[i]; + FOR_EACH(i, 0, bsdf->nbxdfs) bsdf->weights[i] = bsdf->uweights[i]/weights_sum; + bsdf->outdated_weights = 0; +} + static void bsdf_release(ref_T* ref) { struct ssf_bsdf* bsdf = CONTAINER_OF(ref, struct ssf_bsdf, ref); - struct list_node* tmp; - struct list_node* node; ASSERT(ref); - - LIST_FOR_EACH_SAFE(node, tmp, &bsdf->views) { - bsdf_view_destroy(CONTAINER_OF(node, struct ssf_bsdf_view, node)); - } SSF(bsdf_clear(bsdf)); MEM_RM(bsdf->allocator, bsdf); } @@ -63,7 +85,6 @@ ssf_bsdf_create(struct mem_allocator* allocator, struct ssf_bsdf** out_bsdf) } bsdf->allocator = mem_allocator; ref_init(&bsdf->ref); - list_init(&bsdf->views); exit: if(out_bsdf) *out_bsdf = bsdf; @@ -96,12 +117,13 @@ res_T ssf_bsdf_add(struct ssf_bsdf* bsdf, struct ssf_bxdf* bxdf, const double weight) { if(!bsdf || !bxdf || weight < 0) return RES_BAD_ARG; - if(bsdf->ncomponents >= BSDF_MAX_COMPONENTS) return RES_MEM_ERR; + if(bsdf->nbxdfs >= MAX_BxDFs) return RES_MEM_ERR; if(weight == 0) return RES_OK; SSF(bxdf_ref_get(bxdf)); - bsdf->components[bsdf->ncomponents] = bxdf; - bsdf->weights[bsdf->ncomponents] = weight; - ++bsdf->ncomponents; + bsdf->bxdfs[bsdf->nbxdfs] = bxdf; + bsdf->uweights[bsdf->nbxdfs] = weight; + bsdf->outdated_weights = 1; + ++bsdf->nbxdfs; return RES_OK; } @@ -110,10 +132,137 @@ ssf_bsdf_clear(struct ssf_bsdf* bsdf) { size_t i; if(!bsdf) return RES_BAD_ARG; - FOR_EACH(i, 0, bsdf->ncomponents) { - SSF(bxdf_ref_put(bsdf->components[i])); + FOR_EACH(i, 0, bsdf->nbxdfs) { + SSF(bxdf_ref_put(bsdf->bxdfs[i])); } - bsdf->ncomponents = 0; + bsdf->nbxdfs = 0; return RES_OK; } +double +ssf_bsdf_sample + (struct ssf_bsdf* bsdf, + const double u, + const double v, + const double wo[3], + const double N[3], + double wi[3], + double* out_pdf) +{ + double probas[MAX_BxDFs]; + double cumul[MAX_BxDFs]; + double reflectivity; + double pdf; + double rho; + double r; + size_t i, j; + ASSERT(bsdf && u>=0 && u<1 && v>=0 && v<1 && wi && N && wo && out_pdf); + ASSERT(d3_is_normalized(wo) && d3_is_normalized(N)); + + if(!bsdf->nbxdfs) { + *out_pdf = 0; + return 0; + } + if(bsdf->nbxdfs == 1) { + return ssf_bxdf_sample(bsdf->bxdfs[0], u, v, wo, N, wi, out_pdf); + } + bsdf_normalize_weights(bsdf); + + /* Compute the probabilities to sample each component as well as the + * cumulative. For the i^th, component the proba is `Wi * rho_i(wo)/rho(wo)' + * with Wi the component weight, rho_i(wo) its associated rho wrt to wo, and + * rho the overall rho of the BSDF */ + rho = 0; + FOR_EACH(i, 0, bsdf->nbxdfs) { + probas[i] = bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo); + rho += probas[i]; + } + cumul[0] = (probas[0] /= rho); + FOR_EACH(i, 1, bsdf->nbxdfs) { + probas[i] /= rho; + cumul[i] = probas[i] + cumul[i-1]; + } + + /* Sample a component */ + FOR_EACH(i, 0, bsdf->nbxdfs-1) { + if(u >= cumul[i]) break; + } + + /* Rescale the random number u in [0, 1) */ + r = (u - cumul[i]) / nextafter(cumul[i+1] - cumul[i], DBL_MAX); + + /* Sample a direction from the selected component */ + reflectivity = ssf_bxdf_sample(bsdf->bxdfs[i], r, v, wo, N, wi, &pdf); + + /* Compute the overall probabilities to sample this direction and the overall + * reflectivity along this direction */ + pdf *= probas[i]; + reflectivity *= bsdf->weights[i]; + FOR_EACH(j, 0, bsdf->nbxdfs) { + if(i == j) continue; + pdf += probas[j] * ssf_bxdf_pdf(bsdf->bxdfs[j], wo, N, wi); + reflectivity += bsdf->weights[j] * ssf_bxdf_eval(bsdf->bxdfs[j], wo, N, wi); + } + *out_pdf = pdf; +#ifndef NDEBUG + { + const double R = rho * pdf / fabs(d3_dot(wi, N)); + ASSERT(eq_eps(R, reflectivity, 1.e-6)); + } +#endif + return reflectivity; +} + +double +ssf_bsdf_pdf + (struct ssf_bsdf* bsdf, + const double wo[3], + const double N[3], + const double wi[3]) +{ + double pdf = 0; + double rho = 0; + size_t i; + ASSERT(bsdf); + bsdf_normalize_weights(bsdf); + FOR_EACH(i, 0, bsdf->nbxdfs) { + const double tmp = bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo); + pdf += ssf_bxdf_pdf(bsdf->bxdfs[i], wo, N, wi) * tmp; + rho += tmp; + } + pdf /= rho; + return pdf; +} + +double +ssf_bsdf_eval + (struct ssf_bsdf* bsdf, + const double wo[3], + const double N[3], + const double wi[3]) +{ + double R = 0; + size_t i; + ASSERT(bsdf); + bsdf_normalize_weights(bsdf); + FOR_EACH(i, 0, bsdf->nbxdfs) { + R += bsdf->weights[i] * ssf_bxdf_eval(bsdf->bxdfs[i], wo, N, wi); + } + return R; +} + +double +ssf_bsdf_rho + (struct ssf_bsdf* bsdf, + const double wo[3]) +{ + double rho = 0; + size_t i; + ASSERT(bsdf); + bsdf_normalize_weights(bsdf); + FOR_EACH(i, 0, bsdf->nbxdfs) { + rho += bsdf->weights[i] * ssf_bxdf_rho(bsdf->bxdfs[i], wo); + } + return rho; +} + diff --git a/src/ssf_bsdf_c.h b/src/ssf_bsdf_c.h @@ -16,24 +16,7 @@ #ifndef SSF_BSDF_C_H #define SSF_BSDF_C_H -#include <rsys/list.h> -#include <rsys/ref_count.h> -struct ssf_bxdf; -struct mem_allocator; - -#define BSDF_MAX_COMPONENTS 8 - -struct ssf_bsdf { - struct list_node views; /* Pool of available ssf_bsdf_view */ - struct ssf_bxdf* components[BSDF_MAX_COMPONENTS]; - /* Unormalized weights to sample the components */ - double weights[BSDF_MAX_COMPONENTS]; - size_t ncomponents; - - ref_T ref; - struct mem_allocator* allocator; -}; #endif /* SSF_BSDF_C_H */ diff --git a/src/ssf_bsdf_view.c b/src/ssf_bsdf_view.c @@ -1,237 +0,0 @@ -/* Copyright (C) |Meso|Star> 2016 (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_bsdf_view_c.h" - -#include <rsys/double3.h> -#include <rsys/mem_allocator.h> - -#include <float.h> -#include <math.h> - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static void -bsdf_view_release(ref_T* ref) -{ - struct ssf_bsdf_view* view; - size_t i; - ASSERT(ref); - view = CONTAINER_OF(ref, struct ssf_bsdf_view, ref); - - /* Release the BxDFs */ - FOR_EACH(i, 0, view->ncomponents) { - SSF(bxdf_ref_put(view->components[i])); - } - view->ncomponents = 0; - - /* Do not physically release the memory of the view. Add it to the pool of - * available BSDF views */ - list_add(&view->bsdf->views, &view->node); - SSF(bsdf_ref_put(view->bsdf)); -} - -/******************************************************************************* - * Exported functions - ******************************************************************************/ -res_T -ssf_bsdf_view_create(struct ssf_bsdf* bsdf, struct ssf_bsdf_view** out_view) -{ - struct ssf_bsdf_view* view = NULL; - double weights_sum; - size_t i; - res_T res = RES_OK; - - if(!bsdf || !out_view) { - res = RES_BAD_ARG; - goto error; - } - - if(!is_list_empty(&bsdf->views)) { - /* Retrieve an already allocated BSDF view */ - view = CONTAINER_OF(list_head(&bsdf->views), struct ssf_bsdf_view, node); - list_del(&view->node); - ref_get(&view->ref); - } else { /* Allocate a new BSDF view */ - view = MEM_CALLOC(bsdf->allocator, 1, sizeof(struct ssf_bsdf_view)); - if(!view) { - res = RES_MEM_ERR; - goto error; - } - list_init(&view->node); - ref_init(&view->ref); - } - SSF(bsdf_ref_get(bsdf)); - view->bsdf = bsdf; - view->ncomponents = bsdf->ncomponents; - - if(!view->ncomponents) - goto exit; - - /* Get a reference onto the BxDFs and compute the overall weight sum */ - weights_sum = 0; - FOR_EACH(i, 0, view->ncomponents) { - SSF(bxdf_ref_get(bsdf->components[i])); - view->components[i] = bsdf->components[i]; - weights_sum += bsdf->weights[i]; - } - - /* Compute the probabilities of the components and the cumulative */ - view->probas[0] = bsdf->weights[0] / weights_sum; - view->cumulative[0] = view->probas[0]; - FOR_EACH(i, 1, view->ncomponents) { - view->probas[i] = bsdf->weights[i] / weights_sum; - view->cumulative[i] = view->cumulative[i-1] + view->probas[i]; - } -exit: - if(out_view) *out_view = view; - return res; -error: - if(view) { - SSF(bsdf_view_ref_put(view)); - view = NULL; - } - goto exit; -} - -res_T -ssf_bsdf_view_ref_get(struct ssf_bsdf_view* view) -{ - if(!view) return RES_BAD_ARG; - ref_get(&view->ref); - return RES_OK; -} - -res_T -ssf_bsdf_view_ref_put(struct ssf_bsdf_view* view) -{ - if(!view) return RES_BAD_ARG; - ref_put(&view->ref, bsdf_view_release); - return RES_OK; -} - -/* TODO fix this */ -double -ssf_bsdf_view_sample - (struct ssf_bsdf_view* view, - const double u, - const double v, - const double wo[3], - const double N[3], - double wi[3], - double* pdf) -{ - double reflectivity; - double w; - size_t i, icomponent; - ASSERT(view && u>=0 && u<1 && v>=0 && v<1 && wi && N && wo); - ASSERT(d3_is_normalized(wo) && d3_is_normalized(N)); - - if(!view->ncomponents) return 0; - if(view->ncomponents == 1) { - return ssf_bxdf_sample(view->components[0], u, v, wo, N, wi, pdf); - } - - /* Sample a component */ - FOR_EACH(icomponent, 0, view->ncomponents-1) { - if(u >= view->cumulative[icomponent]) break; - } - - /* Rescale the random number to reuse it */ - w = (u - view->cumulative[icomponent] )/ nextafter - (view->cumulative[icomponent+1] - view->cumulative[icomponent], DBL_MAX); - - /* Sample the component */ - reflectivity = ssf_bxdf_sample - (view->components[icomponent], w, v, wo, N, wi, pdf); - if(reflectivity == 0) return 0; - - *pdf *= view->probas[icomponent]; - reflectivity *= *pdf; - - /* Add the contribution of the others components */ - FOR_EACH(i, 0, view->ncomponents) { - if(i == icomponent) continue; - *pdf += ssf_bxdf_pdf(view->components[i], wo, N, wi) * view->probas[i]; - reflectivity += ssf_bxdf_eval(view->components[i], wo, N, wi) * view->probas[i]; - } - return reflectivity / *pdf; -} - -double -ssf_bsdf_view_eval - (struct ssf_bsdf_view* view, - const double wo[3], - const double N[3], - const double wi[3]) -{ - double R = 0; - size_t i; - ASSERT(view && wi && wo); - ASSERT(d3_is_normalized(wi) && d3_is_normalized(wo)); - - FOR_EACH(i, 0, view->ncomponents) { - R += ssf_bxdf_eval(view->components[i], wo, N, wi) * view->probas[i]; - } - return R; -} - -double -ssf_bsdf_view_pdf - (struct ssf_bsdf_view* view, - const double wo[3], - const double N[3], - const double wi[3]) -{ - double pdf = 0; - size_t i; - ASSERT(view && wi && wo); - ASSERT(d3_is_normalized(wi) && d3_is_normalized(wo)); - - FOR_EACH(i, 0, view->ncomponents) { - pdf += ssf_bxdf_pdf(view->components[i], wo, N, wi) * view->probas[i]; - } - return pdf; -} - -double -ssf_bsdf_view_rho - (struct ssf_bsdf_view* view, - const double wo[3]) -{ - double rho = 0; - size_t i; - ASSERT(view && wo); - FOR_EACH(i, 0, view->ncomponents) { - rho += ssf_bxdf_rho(view->components[i], wo) * view->probas[i]; - } - return rho; -} - -/******************************************************************************* - * Local functions - ******************************************************************************/ -void -bsdf_view_destroy(struct ssf_bsdf_view* view) -{ - ASSERT(view); - list_del(&view->node); - MEM_RM(view->bsdf->allocator, view); -} - diff --git a/src/ssf_bsdf_view_c.h b/src/ssf_bsdf_view_c.h @@ -1,38 +0,0 @@ -/* Copyright (C) |Meso|Star> 2016 (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/>. */ - -#ifndef SSF_BSDF_VIEW_C_H -#define SSF_BSDF_VIEW_C_H - -#include "ssf_bsdf_c.h" - -#include <rsys/list.h> -#include <rsys/ref_count.h> - -struct ssf_bsdf_view { - struct list_node node; /* Attachment point to the BSDF views pool */ - - struct ssf_bxdf* components[BSDF_MAX_COMPONENTS]; - double probas[BSDF_MAX_COMPONENTS]; /*Probabilities to sample the components*/ - double cumulative[BSDF_MAX_COMPONENTS]; /* Cumulative of probas */ - size_t ncomponents; - - ref_T ref; - struct ssf_bsdf* bsdf; -}; - -extern LOCAL_SYM void bsdf_view_destroy(struct ssf_bsdf_view* view); - -#endif /* SSF_BSDF_VIEW_C_H */ diff --git a/src/test_ssf_bsdf.c b/src/test_ssf_bsdf.c @@ -21,13 +21,17 @@ int main(int argc, char** argv) { + const size_t NSTEPS = 100000; struct mem_allocator allocator; struct ssf_bsdf* bsdf; struct ssf_bxdf* bxdf; - struct ssf_bsdf_view* view; + struct ssf_bxdf* bxdf2; double wo[3], wi[3], pdf; + double rho; double N[3]; - double R; + double E, V, SE, R; + double sum, sqr_sum; + size_t i; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -44,36 +48,61 @@ main(int argc, char** argv) CHECK(ssf_bsdf_create(&allocator, &bsdf), RES_OK); CHECK(ssf_bxdf_create(&allocator, &ssf_specular_reflection, &bxdf), RES_OK); + CHECK(ssf_bxdf_create(&allocator, &ssf_specular_reflection, &bxdf2), RES_OK); CHECK(ssf_specular_reflection_setup(bxdf, 0.123), RES_OK); + CHECK(ssf_specular_reflection_setup(bxdf2, 0.6789), RES_OK); CHECK(ssf_bsdf_add(NULL, NULL, -1.0), RES_BAD_ARG); CHECK(ssf_bsdf_add(bsdf, NULL, -1.0), RES_BAD_ARG); CHECK(ssf_bsdf_add(NULL, bxdf, -1.0), RES_BAD_ARG); CHECK(ssf_bsdf_add(bsdf, bxdf, -1.0), RES_BAD_ARG); - CHECK(ssf_bsdf_add(NULL, NULL, 1.0), RES_BAD_ARG); - CHECK(ssf_bsdf_add(bsdf, NULL, 1.0), RES_BAD_ARG); - CHECK(ssf_bsdf_add(NULL, bxdf, 1.0), RES_BAD_ARG); - CHECK(ssf_bsdf_add(bsdf, bxdf, 1.0), RES_OK); - - CHECK(ssf_bsdf_view_create(bsdf, &view), RES_OK); + CHECK(ssf_bsdf_add(NULL, NULL, 1), RES_BAD_ARG); + CHECK(ssf_bsdf_add(bsdf, NULL, 1), RES_BAD_ARG); + CHECK(ssf_bsdf_add(NULL, bxdf, 1), RES_BAD_ARG); + CHECK(ssf_bsdf_add(bsdf, bxdf, 1), RES_OK); d3_normalize(wo, d3(wo, 1, 1, 0)); d3(N, 0.0, 1.0, 0.0); - R = ssf_bsdf_view_sample(view, 0.5, 0.5, wo, N, wi, &pdf); + R = ssf_bsdf_sample(bsdf, 0.5, 0.5, wo, N, wi, &pdf); CHECK(eq_eps(R, 0.123/d3_dot(wi, N), 1.e-6), 1); - CHECK(ssf_bsdf_view_eval(view, wo, N, wi), 0); - CHECK(ssf_bsdf_view_pdf(view, wo, N, wi), 0); + CHECK(ssf_bsdf_eval(bsdf, wo, N, wi), 0); + CHECK(ssf_bsdf_pdf(bsdf, wo, N, wi), 0); + + CHECK(ssf_specular_reflection_setup(bxdf, 0.1234), RES_OK); + R = ssf_bsdf_sample(bsdf, 0.5, 0.5, wo, N, wi, &pdf); + CHECK(eq_eps(R, 0.1234 / d3_dot(wi, N), 1.e-6), 1); + + CHECK(ssf_bsdf_add(bsdf, bxdf2, 1), RES_OK); + + /* Estimate the BSDF rho */ + sum = 0; + sqr_sum = 0; + d3(wo, 0, 1, 0); + FOR_EACH(i, 0, NSTEPS) { + const double u = rand_canonic(); + const double v = rand_canonic(); + double weight = ssf_bsdf_sample(bsdf, u, v, wo, N, wi, &pdf); + weight *= d3_dot(N, wi) / pdf; + CHECK(ssf_bsdf_eval(bsdf, wo, N, wi), 0); + CHECK(ssf_bsdf_pdf(bsdf, wo, N, wi), 0); + sum += weight; + sqr_sum += weight*weight; + } + E = sum / (double)NSTEPS; + V = sqr_sum / (double)NSTEPS - E*E; + SE = sqrt(V / (double)NSTEPS); + rho = ssf_bsdf_rho(bsdf, wo); + CHECK(eq_eps(rho, E, SE), 1); CHECK(ssf_bsdf_clear(NULL), RES_BAD_ARG); CHECK(ssf_bsdf_clear(bsdf), RES_OK); - CHECK(ssf_bsdf_view_ref_put(view), RES_OK); - CHECK(ssf_bsdf_view_create(bsdf, &view), RES_OK); - CHECK(ssf_bsdf_view_sample(view, 0, 0, wo, N, wi, &pdf), 0.0); + CHECK(ssf_bsdf_sample(bsdf, 0, 0, wo, N, wi, &pdf), 0.0); + CHECK(pdf, 0.0); CHECK(ssf_bsdf_ref_put(bsdf), RES_OK); CHECK(ssf_bxdf_ref_put(bxdf), RES_OK); - CHECK(ssf_bsdf_view_ref_put(view), RES_OK); + CHECK(ssf_bxdf_ref_put(bxdf2), RES_OK); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_ssf_bsdf_view.c b/src/test_ssf_bsdf_view.c @@ -1,111 +0,0 @@ -/* Copyright (C) |Meso|Star> 2016 (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 <rsys/double3.h> - -int -main(int argc, char** argv) -{ - const size_t NSTEPS = 100000; - struct ssf_bxdf* bxdf; - struct ssf_bxdf* bxdf2; - struct ssf_bsdf* bsdf; - struct ssf_bsdf_view* view; - struct mem_allocator allocator; - double N[3], wi[3], wo[3]; - double pdf; - double R; - double rho; - double E, V, SE; - double sum, sqr_sum; - size_t i; - (void)argc, (void)argv; - - mem_init_proxy_allocator(&allocator, &mem_default_allocator); - - CHECK(ssf_bsdf_create(&allocator, &bsdf), RES_OK); - CHECK(ssf_bxdf_create(&allocator, &ssf_specular_reflection, &bxdf), RES_OK); - CHECK(ssf_bxdf_create(&allocator, &ssf_specular_reflection, &bxdf2), RES_OK); - CHECK(ssf_specular_reflection_setup(bxdf, 1.0), RES_OK); - CHECK(ssf_specular_reflection_setup(bxdf2, 0.6789), RES_OK); - - CHECK(ssf_bsdf_view_create(NULL, NULL), RES_BAD_ARG); - CHECK(ssf_bsdf_view_create(bsdf, NULL), RES_BAD_ARG); - CHECK(ssf_bsdf_view_create(NULL, &view), RES_BAD_ARG); - CHECK(ssf_bsdf_view_create(bsdf, &view), RES_OK); - - d3_normalize(wo, d3(wo, 1, 1, 0)); - d3(N, 0.0, 1.0, 0.0); - CHECK(ssf_bsdf_view_sample(view, 0.5, 0.5, wo, N, wi, &pdf), 0.0); - - CHECK(ssf_bsdf_add(bsdf, bxdf, 0.5), RES_OK); - CHECK(ssf_bsdf_view_sample(view, 0.5, 0.5, wo, N, wi, &pdf), 0.0); - CHECK(ssf_bsdf_view_ref_put(view), RES_OK); - CHECK(ssf_bsdf_view_create(bsdf, &view), RES_OK); - R = ssf_bsdf_view_sample(view, 0.5, 0.5, wo, N, wi, &pdf); - CHECK(eq_eps(R, 1.0 / d3_dot(wi, N), 1.e-6), 1); - CHECK(pdf, 1); - - CHECK(ssf_specular_reflection_setup(bxdf, 0.1234), RES_OK); - R = ssf_bsdf_view_sample(view, 0.5, 0.5, wo, N, wi, &pdf); - CHECK(eq_eps(R, 0.1234 / d3_dot(wi, N), 1.e-6), 1); - - CHECK(ssf_bsdf_add(bsdf, bxdf2, 0.5), RES_OK); - FOR_EACH(i, 0, NSTEPS) { - R = ssf_bsdf_view_sample(view, 0.5, 0.5, wo, N, wi, &pdf); - CHECK(eq_eps(R, 0.1234 / d3_dot(wi, N), 1.e-6), 1); - } - CHECK(ssf_bsdf_view_ref_put(view), RES_OK); - CHECK(ssf_bsdf_view_create(bsdf, &view), RES_OK); - - sum = 0; - sqr_sum = 0; - d3(wo, 0, 1, 0); - FOR_EACH(i, 0, NSTEPS) { - const double u = rand_canonic(); - const double v = rand_canonic(); - const double weight = ssf_bsdf_view_sample(view, u, v, wo, N, wi, &pdf); - CHECK(ssf_bsdf_view_eval(view, wo, N, wi), 0); - CHECK(ssf_bsdf_view_pdf(view, wo, N, wi), 0); - sum += weight; - sqr_sum += weight*weight; - } - E = sum / (double)NSTEPS; - V = sqr_sum / (double)NSTEPS - E*E; - SE = sqrt(V / (double)NSTEPS); - CHECK(eq_eps((0.1234+0.6789) * 0.5, E, SE), 1); - - rho = ssf_bsdf_view_rho(view, wo); - CHECK(eq_eps(rho, (0.1234+0.6789)*0.5, 1.e-6), 1); - - CHECK(ssf_bsdf_view_ref_get(NULL), RES_BAD_ARG); - CHECK(ssf_bsdf_view_ref_get(view), RES_OK); - CHECK(ssf_bsdf_view_ref_put(NULL), RES_BAD_ARG); - CHECK(ssf_bsdf_view_ref_put(view), RES_OK); - CHECK(ssf_bsdf_view_ref_put(view), RES_OK); - - CHECK(ssf_bxdf_ref_put(bxdf), RES_OK); - CHECK(ssf_bxdf_ref_put(bxdf2), RES_OK); - CHECK(ssf_bsdf_ref_put(bsdf), RES_OK); - - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); - CHECK(mem_allocated_size(), 0); - return 0; -} -