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:
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);