commit be1dcfd4054ae0060e8e10268ae274a90dc65f77
parent 03d119e65f2a5e4eed65374bffe1066b45abba19
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 7 Sep 2016 11:47:32 +0200
Implement the ssf_bsdf API
Diffstat:
3 files changed, 188 insertions(+), 1 deletion(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -47,6 +47,7 @@ set(SSF_FILES_DOC COPYING README.md)
set(SSF_FILES_INC_API ssf.h)
set(SSF_FILES_INC ssf_bxdf_c.h)
set(SSF_FILES_SRC
+ ssf_bsdf.c
ssf_bxdf.c
ssf_specular_reflection.c)
rcmake_prepend_path(SSF_FILES_SRC ${SSF_SOURCE_DIR})
diff --git a/src/ssf.h b/src/ssf.h
@@ -77,7 +77,8 @@ ssf_bsdf_sample
const double v, /* Canonical number */
const double w[3], /* Incoming direction */
const double N[3], /* Normalized normal */
- double dir[4]); /* Sampled direction. The PDF is stored in dir[3] */
+ double dir[4], /* Sampled direction. The PDF is stored in dir[3] */
+ double* radiance); /* Sampled radiance */
/*******************************************************************************
* BxDF API - Bidirecitonal <Reflectance|Transmittance> distribution function.
diff --git a/src/ssf_bsdf.c b/src/ssf_bsdf.c
@@ -0,0 +1,185 @@
+/* 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_bxdf_c.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];
+ size_t nbxdfs;
+
+ ref_T ref;
+ struct mem_allocator* allocator;
+};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+bsdf_release(ref_T* ref)
+{
+ struct ssf_bsdf* bsdf = CONTAINER_OF(ref, struct ssf_bsdf, ref);
+ ASSERT(ref);
+ SSF(bsdf_clear(bsdf));
+ MEM_RM(bsdf->allocator, bsdf);
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+ssf_bsdf_create(struct mem_allocator* allocator, struct ssf_bsdf** out_bsdf)
+{
+ struct mem_allocator* mem_allocator = NULL;
+ struct ssf_bsdf* bsdf = NULL;
+ res_T res = RES_OK;
+
+ if(!out_bsdf) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ mem_allocator = allocator ? allocator : &mem_default_allocator;
+ bsdf = MEM_CALLOC(mem_allocator, 1, sizeof(struct ssf_bsdf));
+ if(!bsdf) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ bsdf->allocator = mem_allocator;
+ ref_init(&bsdf->ref);
+
+exit:
+ if(out_bsdf) *out_bsdf = bsdf;
+ return res;
+error:
+ if(bsdf) {
+ SSF(bsdf_ref_put(bsdf));
+ bsdf = NULL;
+ }
+ goto exit;
+}
+
+res_T
+ssf_bsdf_ref_get(struct ssf_bsdf* bsdf)
+{
+ if(!bsdf) return RES_BAD_ARG;
+ ref_get(&bsdf->ref);
+ return RES_OK;
+}
+
+res_T
+ssf_bsdf_ref_put(struct ssf_bsdf* bsdf)
+{
+ if(!bsdf) return RES_BAD_ARG;
+ ref_put(&bsdf->ref, bsdf_release);
+ return RES_OK;
+}
+
+res_T
+ssf_bsdf_add(struct ssf_bsdf* bsdf, struct ssf_bxdf* bxdf)
+{
+ if(!bsdf || !bxdf) return RES_BAD_ARG;
+ if(bsdf->nbxdfs >= MAX_BxDFs) return RES_MEM_ERR;
+ SSF(bxdf_ref_get(bxdf));
+ bsdf->bxdfs[bsdf->nbxdfs] = bxdf;
+ ++bsdf->nbxdfs;
+ return RES_OK;
+}
+
+
+res_T
+ssf_bsdf_clear(struct ssf_bsdf* bsdf)
+{
+ size_t i;
+ if(!bsdf) return RES_BAD_ARG;
+ FOR_EACH(i, 0, bsdf->nbxdfs) {
+ SSF(bxdf_ref_put(bsdf->bxdfs[i]));
+ }
+ bsdf->nbxdfs = 0;
+ return RES_OK;
+}
+
+res_T
+ssf_bsdf_sample
+ (struct ssf_bsdf* bsdf,
+ const double u,
+ const double v,
+ const double w[3],
+ const double N[3],
+ double dir[4],
+ double* radiance)
+{
+ const size_t PDF = 3;
+ double radiances[MAX_BxDFs];
+ double dirs[MAX_BxDFs][4];
+ double probas[MAX_BxDFs];
+ double cumul[MAX_BxDFs];
+ double probas_sum;
+ size_t i, n;
+ res_T res = RES_OK;
+
+ if(!bsdf || u<0 || u>=1 || v<0 || v>=1 || !w || !N || !dir || !radiance) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Build the probability distribution by sampling each BRDF */
+ n = 0;
+ probas_sum = 0.0f;
+ FOR_EACH(i, 0, bsdf->nbxdfs) {
+ struct ssf_bxdf* bxdf = bsdf->bxdfs[i];
+
+ radiances[n] = bxdf->sample(bxdf->data, u, v, w, N, dirs[n]);
+ if(radiances[n] <= 0 || dirs[n][PDF] <= 0)
+ continue; /* Discard BxDF */
+
+ probas[n] = radiances[n] / dirs[n][PDF];
+ probas_sum += probas[n];
+ ++n;
+ }
+
+ if(!n) { /* No valid BxDF to sample */
+ d4_splat(dir, 0);
+ *radiance = 0;
+ goto exit;
+ }
+
+ /* Normalize the probability distribution */
+ FOR_EACH(i, 0, n) probas[i] /= probas_sum;
+
+ /* Compute the cumulative */
+ cumul[0] = probas[0];
+ cumul[n-1] = 1.f;
+ FOR_EACH(i, 1, n-1) cumul[i] = cumul[i-1] + probas[i];
+
+ /* Finally sample the distribution */
+ FOR_EACH(i, 0, n-1) if(u <= cumul[i]) break;
+ d4_set(dir, dirs[i]);
+ *radiance = radiances[i];
+
+exit:
+ return res;
+error:
+ d4_splat(dir, -DBL_MAX);
+ *radiance = -DBL_MAX;
+ goto exit;
+}
+