commit a80a7778c48e5f4585271766956e7d7fe9f39296
parent 7a23d2203659d3a9d5cabd03dc92d4c3f35617cb
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 18 Jul 2018 15:32:49 +0200
Add the Phase Function API
Implement the Henyey & Greenstein built-in phase function.
Diffstat:
| M | cmake/CMakeLists.txt | | | 5 | ++++- |
| M | src/ssf.h | | | 103 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
| A | src/ssf_phase.c | | | 158 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/ssf_phase_c.h | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
| A | src/ssf_phase_hg.c | | | 107 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 410 insertions(+), 6 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -49,7 +49,8 @@ set(SSF_FILES_INC
ssf_bsdf_c.h
ssf_fresnel_c.h
ssf_microfacet_distribution_c.h
- ssf_optics.h)
+ ssf_optics.h
+ ssf_phase_c.h)
set(SSF_FILES_SRC
ssf_beckmann_distribution.c
ssf_blinn_distribution.c
@@ -62,6 +63,8 @@ set(SSF_FILES_SRC
ssf_lambertian_reflection.c
ssf_microfacet_distribution.c
ssf_microfacet_reflection.c
+ ssf_phase.c
+ ssf_phase_hg.c
ssf_pillbox_distribution.c
ssf_specular_dielectric_dielectric_interface.c
ssf_specular_reflection.c
diff --git a/src/ssf.h b/src/ssf.h
@@ -46,6 +46,7 @@ struct ssp_rng;
struct ssf_bsdf; /* Bidirectional Scattering Distribution Function */
struct ssf_fresnel; /* Equation of the Fresnel term */
struct ssf_microfacet_distribution;
+struct ssf_phase; /* Phase function */
enum ssf_bxdf_flag {
SSF_REFLECTION = BIT(0),
@@ -76,7 +77,7 @@ struct ssf_bsdf_type {
int* type, /* Type of the sampled component. Combination of ssf_bxdf_flag */
double* pdf); /* PDF to sample wi wrt wo */
- /* Evaluate the BxDF wrt to wo and wi */
+ /* Evaluate the BxDF wrt `wo' and `wi' */
double
(*eval)
(void* bsdf,
@@ -148,6 +149,42 @@ struct ssf_microfacet_distribution_type {
static const struct ssf_microfacet_distribution_type
SSF_MICROFACET_DISTRIBUTION_TYPE_NULL = SSF_MICROFACET_DISTRIBUTION_TYPE_NULL__;
+/* Generic phase function type descriptor. Note that by convention the outgoing
+ * direction `wo' and the incoming direction `wi' point outward the scattering
+ * point */
+struct ssf_phase_type {
+ res_T (*init)(struct mem_allocator* allocator, void* bsdf);
+ void (*release)(void* bsdf);
+
+ /* Sample a direction `wi' wrt `wo'. TODO comment the PDF and the return
+ * value */
+ double
+ (*sample)
+ (void* phase,
+ struct ssp_rng* rng, /* Random number generator */
+ const double wo[3], /* Normalized outgoing direction */
+ double wi[3], /* Sampled normalized incoming direction */
+ double* pdf); /* PDF to sample wi wrt wo */
+
+ /* Evaluate the phase function wrt `wo' and `wi' */
+ double
+ (*eval)
+ (void* phase,
+ const double wo[3], /* Normalized outgoing direction */
+ const double wi[3]); /* Normalized incoming direction */
+
+ double
+ (*pdf)
+ (void* phase,
+ const double wo[3], /* Normalized outgoing direction */
+ const double wi[3]); /* Normalized incoming direction */
+
+ size_t sizeof_phase; /* In Bytes */
+ size_t alignof_phase; /* In Bytes */
+};
+#define SSF_PHASE_TYPE_NULL__ {NULL,NULL,NULL,NULL,NULL,0,1}
+static const struct ssf_phase_type SSF_PHASE_TYPE_NULL = SSF_PHASE_TYPE_NULL__;
+
BEGIN_DECLS
/*******************************************************************************
@@ -256,10 +293,17 @@ SSF_API const struct ssf_microfacet_distribution_type ssf_beckmann_distribution;
SSF_API const struct ssf_microfacet_distribution_type ssf_blinn_distribution;
/* Pillbox microfacet distribution.
- * D(wh) = */
+ * D(wh) = TODO comment this */
SSF_API const struct ssf_microfacet_distribution_type ssf_pillbox_distribution;
/*******************************************************************************
+ * Built-in phase functions
+ ******************************************************************************/
+/* Henyey & Greenstein phase function.
+ * TODO add comments */
+SSF_API const struct ssf_phase_type ssf_phase_hg;
+
+/*******************************************************************************
* BSDF API - Bidirectional Scattering Distribution Function. Describes the way
* the light is scattered by a surface. Note that by convention the outgoing
* direction `wo' and the incoming direction `wi' point outward the surface.
@@ -282,7 +326,7 @@ ssf_bsdf_ref_put
(struct ssf_bsdf* bsdf);
/* Sample a direction `wi' wrt `wo' whose pdf is BSDF(wo, wi) |wi.n|. Return
- * the directionnal reflectance, i.e. pdf to be reflected in *any* direction
+ * the directional reflectance, i.e. pdf to be reflected in *any* direction
* wrt to the incident direction `wi' */
SSF_API double
ssf_bsdf_sample
@@ -304,9 +348,9 @@ ssf_bsdf_eval
SSF_API double /* Probability density function*/
ssf_bsdf_pdf
(struct ssf_bsdf* bsdf,
- const double wo[3], /* Normalized incoming direction */
+ const double wo[3], /* Normalized outgoing direction */
const double N[3], /* Normalized surface normal */
- const double wi[3]); /* Normalized outgoing direction */
+ const double wi[3]); /* Normalized incoming direction */
SSF_API res_T
ssf_bsdf_get_data
@@ -344,6 +388,55 @@ ssf_specular_dielectric_dielectric_interface_setup
const double eta_t);
/*******************************************************************************
+ * Phase function API - Describes the way the light is scattered in a medium.
+ * Note that by convention the outgoing direction `wo' and the incoming
+ * direction `wi' point outward the scattering position.
+ ******************************************************************************/
+SSF_API res_T
+ssf_phase_create
+ (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */
+ const struct ssf_phase_type* type,
+ struct ssf_phase** phase);
+
+SSF_API res_T
+ssf_phase_ref_get
+ (struct ssf_phase* phase);
+
+SSF_API res_T
+ssf_phase_ref_put
+ (struct ssf_phase* phase);
+
+SSF_API double
+ssf_phase_sample
+ (struct ssf_phase* phase,
+ struct ssp_rng* rng, /* Random number generator */
+ const double wo[3], /* Normalized outgoing direction */
+ double wi[3], /* Sampled normalized incoming direction */
+ double* pdf); /* PDF to sample wi wrt wo */
+
+SSF_API double
+ssf_phase_eval
+ (struct ssf_phase* phase,
+ const double wo[3], /* Normalized outgoing direction */
+ const double wi[3]); /* Normalized incoming direction */
+
+SSF_API double
+ssf_phase_pdf
+ (struct ssf_phase* phase,
+ const double wo[3], /* Normalized outgoing direction */
+ const double wi[3]); /* Normalized incoming direction */
+
+SSF_API res_T
+ssf_phase_get_data
+ (struct ssf_phase* bsdf,
+ void** data);
+
+SSF_API res_T
+ssf_phase_hg_setup
+ (struct ssf_phase* phase,
+ const double g); /* Asymmetric parameter */
+
+/*******************************************************************************
* Fresnel API - Define the equation of the fresnel term
******************************************************************************/
SSF_API res_T
diff --git a/src/ssf_phase.c b/src/ssf_phase.c
@@ -0,0 +1,158 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (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_phase_c.h"
+
+#include <rsys/double3.h>
+#include <rsys/math.h>
+#include <rsys/mem_allocator.h>
+
+#include <string.h>
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static INLINE int
+check_phase_type(const struct ssf_phase_type* type)
+{
+ return type
+ && type->init
+ && type->release
+ && type->sample
+ && type->eval
+ && type->pdf
+ && IS_POW2(type->alignof_phase);
+}
+
+static void
+phase_release(ref_T* ref)
+{
+ struct ssf_phase* phase = CONTAINER_OF(ref, struct ssf_phase, ref);
+ ASSERT(ref);
+ if(phase->data) {
+ phase->type.release(phase->data);
+ MEM_RM(phase->allocator, phase->data);
+ }
+ MEM_RM(phase->allocator, phase);
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+ssf_phase_create
+ (struct mem_allocator* allocator,
+ const struct ssf_phase_type* type,
+ struct ssf_phase** out_phase)
+{
+ struct mem_allocator* mem_allocator = NULL;
+ struct ssf_phase* phase = NULL;
+ res_T res = RES_OK;
+
+ if(!out_phase || !check_phase_type(type)) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ mem_allocator = allocator ? allocator : &mem_default_allocator;
+ phase = MEM_CALLOC(mem_allocator, 1, sizeof(struct ssf_phase));
+ if(!phase) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ ref_init(&phase->ref);
+ phase->allocator = mem_allocator;
+ phase->type = *type;
+
+ phase->data = MEM_ALLOC_ALIGNED
+ (phase->allocator, phase->type.sizeof_phase, phase->type.alignof_phase);
+ if(!phase->data) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memset(phase->data, 0, phase->type.sizeof_phase);
+ res = phase->type.init(mem_allocator, phase->data);
+ if(res != RES_OK) goto error;
+
+exit:
+ if(out_phase) *out_phase = phase;
+ return res;
+error:
+ if(phase) {
+ SSF(phase_ref_put(phase));
+ phase = NULL;
+ }
+ goto exit;
+}
+
+res_T
+ssf_phase_ref_get(struct ssf_phase* phase)
+{
+ if(!phase) return RES_BAD_ARG;
+ ref_get(&phase->ref);
+ return RES_OK;
+}
+
+res_T
+ssf_phase_ref_put(struct ssf_phase* phase)
+{
+ if(!phase) return RES_BAD_ARG;
+ ref_put(&phase->ref, phase_release);
+ return RES_OK;
+}
+
+double
+ssf_phase_sample
+ (struct ssf_phase* phase,
+ struct ssp_rng* rng,
+ const double wo[3],
+ double wi[3],
+ double* pdf)
+{
+ ASSERT(phase && rng && wo && wi && pdf);
+ ASSERT(d3_is_normalized(wo));
+ return phase->type.sample(phase->data, rng, wo, wi, pdf);
+}
+
+double
+ssf_phase_eval
+ (struct ssf_phase* phase,
+ const double wo[3],
+ const double wi[3])
+{
+ ASSERT(phase && wo && wi);
+ ASSERT(d3_is_normalized(wo) && d3_is_normalized(wi));
+ return phase->type.eval(phase->data, wo, wi);
+}
+
+double
+ssf_phase_pdf
+ (struct ssf_phase* phase,
+ const double wo[3],
+ const double wi[3])
+{
+ ASSERT(phase && wi && wo);
+ ASSERT(d3_is_normalized(wo) && d3_is_normalized(wi));
+ return phase->type.pdf(phase->data, wo, wi);
+}
+
+res_T
+ssf_phase_get_data(struct ssf_phase* phase, void** data)
+{
+ if(!phase || !data) return RES_BAD_ARG;
+ *data = phase->data;
+ return RES_OK;
+}
+
diff --git a/src/ssf_phase_c.h b/src/ssf_phase_c.h
@@ -0,0 +1,43 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (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_PHASE_C_H
+#define SSF_PHASE_C_H
+
+#include "ssf.h"
+#include <rsys/ref_count.h>
+
+struct mem_allocator;
+
+struct ssf_phase {
+ struct ssf_phase_type type;
+ void* data; /* Specific internal data of the pĥase function */
+
+ /* Private data */
+ ref_T ref;
+ struct mem_allocator* allocator;
+};
+
+#define PHASE_TYPE_EQ(A, B) \
+ ( (A)->init == (B)->init \
+ && (A)->release == (B)->release \
+ && (A)->sample == (B)->sample \
+ && (A)->eval == (B)->eval \
+ && (A)->pdf == (B)->pdf \
+ && (A)->sizeof_phase == (B)->sizeof_phase \
+ && (A)->alignof_phase == (B)->alignof_phase)
+
+#endif /* SSF_PHASE_C_H */
+
diff --git a/src/ssf_phase_hg.c b/src/ssf_phase_hg.c
@@ -0,0 +1,107 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (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_phase_c.h"
+
+#include <rsys/double3.h>
+
+#include <star/ssp.h>
+
+struct hg { double g; };
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static res_T
+hg_init(struct mem_allocator* allocator, void* phase)
+{
+ ASSERT(phase);
+ (void)allocator;
+ ((struct hg*)phase)->g = 0.0;
+ return RES_OK;
+}
+
+static void
+hg_release(void* phase)
+{ (void)phase; }
+
+/* HG(theta) = 1/(4*PI) * (1 - g^2) / (1 + g^2 - 2*g*cos(theta))^3/2 */
+static double
+hg_eval(void* data, const double wo[3], const double wi[3])
+{
+ const struct hg* hg = data;
+ double g;
+ double cos_theta;
+ double denom;
+ ASSERT(data && wo && wi);
+ ASSERT(d3_is_normalized(wo) && d3_is_normalized(wi));
+ g = hg->g;
+ cos_theta = d3_dot(wi, wo);
+ denom = 1 + g*g - 2*g*cos_theta;
+ ASSERT(denom != 0);
+ return 1.0/(4.0*PI) * (1 - g*g) / (denom*sqrt(denom));
+}
+
+static double
+hg_sample
+ (void* data,
+ struct ssp_rng* rng,
+ const double wo[3],
+ double wi[3],
+ double* pdf)
+{
+ const struct hg* hg = data;
+ double sample[3];
+ ASSERT(data && wo && wi);
+
+ ssp_ran_sphere_hg(rng, wo, hg->g, sample, pdf);
+ d3_set(wi, sample);
+ return NaN; /* FIXME define return value */
+}
+
+static double
+hg_pdf
+ (void* data,
+ const double wo[3],
+ const double wi[3])
+{
+ const struct hg* hg = data;
+ ASSERT(data && wo && wi);
+ return ssp_ran_sphere_hg_pdf(wo, hg->g, wi);
+}
+
+/*******************************************************************************
+ * Exported symbols
+ ******************************************************************************/
+const struct ssf_phase_type ssf_phase_hg = {
+ hg_init,
+ hg_release,
+ hg_sample,
+ hg_eval,
+ hg_pdf,
+ sizeof(struct hg),
+ ALIGNOF(struct hg)
+};
+
+res_T
+ssf_phase_hg_setup(struct ssf_phase* phase, const double g)
+{
+ if(!phase || g < -1 || g > 1) return RES_BAD_ARG;
+ if(!PHASE_TYPE_EQ(&phase->type, &ssf_phase_hg)) return RES_BAD_ARG;
+ ((struct hg*)phase->data)->g = g;
+ return RES_OK;
+}
+