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 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:
Mcmake/CMakeLists.txt | 5++++-
Msrc/ssf.h | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Asrc/ssf_phase.c | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ssf_phase_c.h | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/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; +} +