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 0423d8686216f088137ee7d1a6bbd3c13acf89f6
parent 9a86fd81ba0a61fbb65e0bf0e757913fc7bc1b85
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 15 Sep 2016 14:57:22 +0200

Add the BSDF views

A BSDF views is the current state of the BSDF composition. On its
creation, it pre-computes the probabilities to sample the components and
the associated cumulative. The BSDF can be now sampled only through this
structure.

Diffstat:
Mcmake/CMakeLists.txt | 3+++
Msrc/ssf.h | 33+++++++++++++++++++++++++++++++--
Msrc/ssf_bsdf.c | 82++++++++++---------------------------------------------------------------------
Asrc/ssf_bsdf_c.h | 39+++++++++++++++++++++++++++++++++++++++
Asrc/ssf_bsdf_view.c | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ssf_bsdf_view_c.h | 38++++++++++++++++++++++++++++++++++++++
Msrc/test_ssf_bsdf.c | 13++++++++++---
7 files changed, 309 insertions(+), 77 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -46,10 +46,13 @@ 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) set(SSF_FILES_SRC ssf_bsdf.c + ssf_bsdf_view.c ssf_bxdf.c ssf_fresnel.c ssf_fresnel_dielectric_conductor.c diff --git a/src/ssf.h b/src/ssf.h @@ -40,6 +40,7 @@ struct mem_allocator; /* Opaque data types */ struct ssf_bsdf; /* Bidirectional Scattering Distribution Function */ +struct ssf_bsdf_view; /* Current state of the BSDF */ struct ssf_bxdf; /* Bidirectional <Reflec|Transmit>tance Distribution Function */ struct ssf_fresnel; /* Equation of the Fresnel term */ @@ -130,15 +131,43 @@ SSF_API res_T ssf_bsdf_clear (struct ssf_bsdf* bsdf); -SSF_API double -ssf_bsdf_sample +/******************************************************************************* + * BSDF view API - State of the BSDF + ******************************************************************************/ +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 /* Reflectivity */ +ssf_bsdf_view_sample + (struct ssf_bsdf_view* view, const double u, /* Canonical number */ const double v, /* Canonical number */ const double wi[3], /* Normalized incoming direction */ const double N[3], /* Normalized normal */ double wo[4]); /* Sampled direction. The PDF is stored in dir[3] */ +SSF_API double /* Reflectivity */ +ssf_bsdf_view_eval + (struct ssf_bsdf_view* view, + const double wi[3], /* Normalized incoming direction */ + const double wo[3]); /* Normalized outgoing direction */ + +SSF_API double +ssf_bsdf_view_pdf + (struct ssf_bsdf_view* view, + const double wi[3], /* Normalized incoming direction */ + const double wo[3]); /* Normalized outgoing direction */ + /******************************************************************************* * Fresnel API - Define the equation of the fresnel term ******************************************************************************/ diff --git a/src/ssf_bsdf.c b/src/ssf_bsdf.c @@ -14,23 +14,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #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_COMPONENTS 8 - -struct ssf_bsdf { - struct ssf_bxdf* components[MAX_COMPONENTS]; - double weights[MAX_COMPONENTS]; - size_t ncomponents; - - ref_T ref; - struct mem_allocator* allocator; -}; /******************************************************************************* * Helper functions @@ -39,7 +29,13 @@ 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); } @@ -67,6 +63,7 @@ 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; @@ -99,7 +96,7 @@ 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 >= MAX_COMPONENTS) return RES_MEM_ERR; + if(bsdf->ncomponents >= BSDF_MAX_COMPONENTS) return RES_MEM_ERR; if(weight == 0) return RES_OK; SSF(bxdf_ref_get(bxdf)); bsdf->components[bsdf->ncomponents] = bxdf; @@ -121,62 +118,3 @@ ssf_bsdf_clear(struct ssf_bsdf* bsdf) return RES_OK; } -double -ssf_bsdf_sample - (struct ssf_bsdf* bsdf, - const double u, - const double v, - const double wi[3], - const double N[3], - double wo[4]) -{ - double probas[MAX_COMPONENTS]; - double cumul[MAX_COMPONENTS]; - double probas_sum; - double reflectivity; - double w; - double pdf; - size_t i, icomponent; - - if(!bsdf->ncomponents) return 0; - if(bsdf->ncomponents == 1) { - return ssf_bxdf_sample(bsdf->components[0], u, v, wi, N, wo); - } - - /* Compute the normalization factor of the BxDF weights */ - probas_sum = 0; - FOR_EACH(icomponent, 0, bsdf->ncomponents) { - probas_sum += bsdf->weights[icomponent]; - } - - /* Compute the components probabilities and the cumulative */ - cumul[0] = probas[0] = bsdf->weights[0] / probas_sum; - FOR_EACH(icomponent, 0, bsdf->ncomponents) { - probas[icomponent] = bsdf->weights[icomponent] / probas_sum; - cumul[icomponent] = cumul[icomponent-1] + probas[icomponent]; - } - /* Sample a component */ - FOR_EACH(icomponent, 0, bsdf->ncomponents-1) { - if(u >= cumul[icomponent]) break; - } - - /* Rescale the random number to reuse it (NOTE why?) */ - w = (u - cumul[icomponent] ) / (cumul[icomponent+1] - cumul[icomponent]); - - /* Sample the component */ - reflectivity = ssf_bxdf_sample(bsdf->components[icomponent], w, v, wi, N, wo); - if(reflectivity == 0) return 0; - - pdf = wo[3] * probas[icomponent]; - reflectivity *= pdf; - - /* Add the contribution of the others components */ - FOR_EACH(i, 0, bsdf->ncomponents) { - if(i == icomponent) continue; - pdf += ssf_bxdf_pdf(bsdf->components[i], wi, wo) * probas[i]; - reflectivity += ssf_bxdf_eval(bsdf->components[i], wi, wo) * probas[i]; - } - wo[3] = pdf; - return reflectivity / pdf; -} - diff --git a/src/ssf_bsdf_c.h b/src/ssf_bsdf_c.h @@ -0,0 +1,39 @@ +/* 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_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 @@ -0,0 +1,178 @@ +/* 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 "ssf_bsdf_view_c.h" + +#include <rsys/mem_allocator.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; + 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[i] / 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; +} + +double +ssf_bsdf_view_sample + (struct ssf_bsdf_view* view, + const double u, + const double v, + const double wi[3], + const double N[3], + double wo[4]) +{ + double reflectivity; + double pdf; + double w; + size_t i, icomponent; + + if(!view->ncomponents) return 0; + if(view->ncomponents == 1) { + return ssf_bxdf_sample(view->components[0], u, v, wi, N, wo); + } + + /* Sample a component */ + FOR_EACH(icomponent, 0, view->ncomponents-1) { + if(u >= view->cumulative[icomponent]) break; + } + + /* Rescale the random number to reuse it (NOTE why?) */ + w = (u - view->cumulative[icomponent] ) + / (view->cumulative[icomponent+1] - view->cumulative[icomponent]); + + /* Sample the component */ + reflectivity = ssf_bxdf_sample(view->components[icomponent], w, v, wi, N, wo); + if(reflectivity == 0) return 0; + + pdf = wo[3] * 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], wi, wo) * view->probas[i]; + reflectivity += ssf_bxdf_eval(view->components[i], wi, wo) * view->probas[i]; + } + wo[3] = pdf; + return reflectivity / pdf; +} + +/******************************************************************************* + * 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 @@ -0,0 +1,38 @@ +/* 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 @@ -24,6 +24,7 @@ main(int argc, char** argv) struct mem_allocator allocator; struct ssf_bsdf* bsdf; struct ssf_bxdf* bxdf; + struct ssf_bsdf_view* view; double w[3]; double N[3]; double dir[4]; @@ -54,17 +55,23 @@ main(int argc, char** argv) 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); + d3_normalize(w, d3(w, -1, -1, 0)); d3(N, 0.0, 1.0, 0.0); - CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, N, dir), 2.0); - CHECK(ssf_bsdf_sample(bsdf, 0.5, 0.5, w, N, dir), 2.0); + CHECK(ssf_bsdf_view_sample(view, 0, 0, w, N, dir), 2.0); + CHECK(ssf_bsdf_view_sample(view, 0.5, 0.5, w, N, dir), 2.0); CHECK(ssf_bsdf_clear(NULL), RES_BAD_ARG); CHECK(ssf_bsdf_clear(bsdf), RES_OK); - CHECK(ssf_bsdf_sample(bsdf, 0, 0, w, N, dir), 0.0); + 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, w, N, dir), 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_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator);