commit 345529bf27857f07d453303a1aaa0a9d2aa46e45
parent 8bef65d76127e7ee1f219f827d2b6d4b139a892c
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Thu, 15 Nov 2018 15:59:36 +0100
Merge branch 'release_0.6'
Diffstat:
46 files changed, 1369 insertions(+), 362 deletions(-)
diff --git a/README.md b/README.md
@@ -1,15 +1,13 @@
# Star-ScatteringFunctions
-This library provides a set of abstractions to describe the way the
-light is scattered at a surface with respect to a geometric optics model. It
-defines interfaces to describe Bidirectional Reflectance|Transmittance
-Distribution Functions (BRDF & BTDF), microfacet distributions and Fresnel
-terms. Several built-in implementation of these interfaces are provided as
-specular, lambertian and microfacet reflections, Beckmann and Blinn microfacet
-distributions, or dielectric/dielectric and dielectric/conductor Fresnel terms.
-
-Finally, complex scattering functions defines as a combination of several BRDFs
-and BTDFs are handled through a Bidirectional Scattering Distribution Function.
+This library provides a set of abstractions to describe the way the light is
+scattered with respect to a geometric optics model. It defines interfaces to
+describe Bidirectional Scattering Distribution Functions, microfacet
+distributions, Fresnel terms and phase functions. Several built-in
+implementation of these interfaces are provided as specular, lambertian and
+microfacet reflections, Beckmann and Blinn microfacet distributions,
+dielectric/dielectric and dielectric/conductor Fresnel terms, Henyey &
+Greenstein and Rayleigh phase functions, etc.
## How to build
@@ -26,6 +24,13 @@ project from the `cmake/CMakeLists.txt` file by appending to the
## Release notes
+### Version 0.6
+
+- Add the phase function API allowing the user to define, sample and evaluate a
+ phase function.
+- Provide built-in implementation of the Henyey & Greenstein, and the Rayleigh
+ phase functions.
+
### Version 0.5
- Add the pillbox microfacet distribution.
@@ -57,9 +62,8 @@ project from the `cmake/CMakeLists.txt` file by appending to the
## License
-Star-ScatteringFunctions is Copyright (C) |Meso|Star> 2016-2017
-(<contact@meso-star.com>). It is a free software released under the
-[OSI](http://opensource.org)-approved GPL v3+ license. You are welcome to
-redistribute it under certain conditions; refer to the COPYING file for
-details.
+Star-ScatteringFunctions is Copyright (C) 2016-2018
+|Meso|Star>(<contact@meso-star.com>). It is a free software released under the
+GPL v3+ license. You are welcome to redistribute it under certain conditions;
+refer to the COPYING file for details.
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+# Copyright (C) 2016-2018 |Meso|Star> (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
@@ -39,7 +39,7 @@ rcmake_append_runtime_dirs(_runtime_dirs RSys StarSP)
# Define targets
################################################################################
set(VERSION_MAJOR 0)
-set(VERSION_MINOR 5)
+set(VERSION_MINOR 6)
set(VERSION_PATCH 0)
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
@@ -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,9 @@ set(SSF_FILES_SRC
ssf_lambertian_reflection.c
ssf_microfacet_distribution.c
ssf_microfacet_reflection.c
+ ssf_phase.c
+ ssf_phase_hg.c
+ ssf_phase_rayleigh.c
ssf_pillbox_distribution.c
ssf_specular_dielectric_dielectric_interface.c
ssf_specular_reflection.c
@@ -112,6 +116,10 @@ if(NOT NO_TEST)
new_test(test_ssf_lambertian_reflection)
new_test(test_ssf_microfacet_distribution)
new_test(test_ssf_microfacet_reflection)
+ new_test(test_ssf_phase)
+ new_test(test_ssf_phase_hg)
+ new_test(test_ssf_phase_rayleigh)
+ new_test(test_ssf_specular_dielectric_dielectric_reflection)
new_test(test_ssf_specular_reflection)
new_test(test_ssf_thin_specular_dielectric)
diff --git a/src/ssf.h b/src/ssf.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -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),
@@ -61,8 +62,8 @@ enum ssf_bxdf_flag {
* consequence the reflected or refracted direction `wi' must point on the same
* side or on the opposite side of `N', respectively. */
struct ssf_bsdf_type {
- res_T (*init)(struct mem_allocator* allocator, void* bsdf);
- void (*release)(void* bsdf);
+ res_T (*init)(struct mem_allocator* allocator, void* bsdf); /* Can be NULL */
+ void (*release)(void* bsdf); /* Can be NULL */
/* Sample a direction `wi' wrt `wo' whose pdf is BSDF(wo, wi) |wi.n|. Return
* the value of BSDF(wo, wi) */
@@ -73,10 +74,10 @@ struct ssf_bsdf_type {
const double wo[3], /* Normalized outgoing direction */
const double N[3], /* Normalized surface normal */
double wi[3], /* Sampled normalized incoming direction */
- int* type, /* Type of the sampled component. Combination of ssf_bxdf_flag */
- double* pdf); /* PDF to sample wi wrt wo */
+ int* type, /* Sampled component. Combination of ssf_bxdf_flag. Can be NULL */
+ double* pdf); /* PDF to sample wi wrt wo. Can be NULL */
- /* Evaluate the BxDF wrt to wo and wi */
+ /* Evaluate the BxDF wrt `wo' and `wi' */
double
(*eval)
(void* bsdf,
@@ -93,15 +94,15 @@ struct ssf_bsdf_type {
const double wi[3]);/* Normalized incoming direction */
size_t sizeof_bsdf; /* In Bytes */
- size_t alignof_bsdf; /* In Bytes */
+ size_t alignof_bsdf; /* In Bytes. Must be a power of 2 */
};
#define SSF_BSDF_TYPE_NULL__ {NULL, NULL, NULL, NULL, NULL, 0, 1}
static const struct ssf_bsdf_type SSF_BXDF_TYPE_NULL = SSF_BSDF_TYPE_NULL__;
/* Generic Fresnel term descriptor */
struct ssf_fresnel_type {
- res_T (*init)(struct mem_allocator* allocator, void* fresnel);
- void (*release)(void* bxdf);
+ res_T (*init)(struct mem_allocator* allocator, void* fresnel); /* Can be NULL */
+ void (*release)(void* bxdf); /* Can be NULL */
double
(*eval)
@@ -109,7 +110,7 @@ struct ssf_fresnel_type {
const double cos_wi_N); /* Cosine between facet normal and incoming dir */
size_t sizeof_fresnel; /* In Bytes */
- size_t alignof_fresnel; /* In Bytes */
+ size_t alignof_fresnel; /* In Bytes. Must be a power of 2 */
};
#define SSF_FRESNEL_TYPE_NULL__ {NULL, NULL, NULL, 0, 1}
static const struct ssf_fresnel_type SSF_FRESNEL_TYPE_NULL =
@@ -117,8 +118,8 @@ static const struct ssf_fresnel_type SSF_FRESNEL_TYPE_NULL =
/* Generic descriptor of a microfacet distribution */
struct ssf_microfacet_distribution_type {
- res_T (*init)(struct mem_allocator* allocator, void* distrib);
- void (*release)(void* distrib);
+ res_T (*init)(struct mem_allocator* allocator, void* distrib); /* Can be NULL */
+ void (*release)(void* distrib); /* Can be NULL */
void
(*sample)
@@ -126,7 +127,7 @@ struct ssf_microfacet_distribution_type {
struct ssp_rng* rng, /* Random number generator */
const double N[3], /* Normalized Z-direction of the distribution */
double wh[3], /* Sampled normalized half vector */
- double* pdf); /* PDF to sample wh */
+ double* pdf); /* PDF to sample wh. Can be NULL */
double
(*eval)
@@ -141,13 +142,49 @@ struct ssf_microfacet_distribution_type {
const double wh[3]); /* Normalized half vector */
size_t sizeof_distribution; /* In Bytes */
- size_t alignof_distribution; /* In Bytes */
+ size_t alignof_distribution; /* In Bytes. Must be a Power of 2 */
};
#define SSF_MICROFACET_DISTRIBUTION_TYPE_NULL__ \
{NULL, NULL, NULL, NULL, NULL, 0, 1}
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. The scattering angle is thus acos(-wo.wi) */
+struct ssf_phase_type {
+ res_T (*init)(struct mem_allocator* allocator, void* bsdf); /*Can be NULL*/
+ void (*release)(void* bsdf); /* Can be NULL */
+
+ /* Sample a direction `wi' wrt `wo'. */
+ void
+ (*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. Can be NULL */
+
+ /* 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 */
+
+ /* Probability density function */
+ 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. Must be a power of 2 */
+};
+#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,23 @@ 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) = */
+ * | 0; if |wh.N| >= m
+ * D(wh) = | 1 / (PI * (1 - cos^2(m))); if |wh.N| < m
+ * with `m' the roughness parameter in ]0, 1] */
SSF_API const struct ssf_microfacet_distribution_type ssf_pillbox_distribution;
/*******************************************************************************
+ * Built-in phase functions.
+ ******************************************************************************/
+/* Henyey & Greenstein phase function normalized to 1; p(wo, wi) == pdf(wo, wi).
+ * p(wo, wi) = 1/(4*PI)* (1-g^2) / (1+g^2-2*g*(-wo.wi))^3/2 */
+SSF_API const struct ssf_phase_type ssf_phase_hg;
+
+/* Rayleigh phase function normalized to 1; p(wo, wi) == pdf(wo, wi).
+ * p(wo, wi) = 3/(16*PI) * (1+(-wo.wi)^2) */
+SSF_API const struct ssf_phase_type ssf_phase_rayleigh;
+
+/*******************************************************************************
* 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 +332,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 +354,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 +394,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 void
+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_beckmann_distribution.c b/src/ssf_beckmann_distribution.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -37,10 +37,6 @@ beckmann_distribution_init(struct mem_allocator* allocator, void* distrib)
return RES_OK;
}
-static void
-beckmann_distribution_release(void* distrib)
-{ (void)distrib; }
-
static double
beckmann_distribution_eval
(void* distrib, const double N[3], const double wh[3])
@@ -96,7 +92,8 @@ beckmann_distribution_sample
dir[1] = sin(phi) * sin_theta;
dir[2] = cos_theta;
d33_muld3(wh, d33_basis(basis, N), dir);
- *pdf = exp(-sin2_theta/(cos2_theta*m2)) / (PI*m2*cos_theta*cos2_theta);
+ ASSERT(d3_dot(wh, N) > 0);
+ if(pdf) *pdf = exp(-sin2_theta/(cos2_theta*m2))/(PI*m2*cos_theta*cos2_theta);
}
static double
@@ -121,7 +118,7 @@ beckmann_distribution_pdf
******************************************************************************/
const struct ssf_microfacet_distribution_type ssf_beckmann_distribution = {
beckmann_distribution_init,
- beckmann_distribution_release,
+ NULL,
beckmann_distribution_sample,
beckmann_distribution_eval,
beckmann_distribution_pdf,
diff --git a/src/ssf_blinn_distribution.c b/src/ssf_blinn_distribution.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -37,10 +37,6 @@ blinn_distribution_init(struct mem_allocator* allocator, void* distrib)
return RES_OK;
}
-static void
-blinn_distribution_release(void* distrib)
-{ (void)distrib; }
-
static double
blinn_distribution_eval(void* distrib, const double N[3], const double wh[3])
{
@@ -68,7 +64,7 @@ blinn_distribution_sample
double cos_theta, sin_theta;
double phi;
double u, v;
- ASSERT(distrib && rng && N && wh && pdf);
+ ASSERT(distrib && rng && N && wh);
ASSERT(d3_is_normalized(N));
u = ssp_rng_canonical(rng);
@@ -82,8 +78,9 @@ blinn_distribution_sample
dir[2] = cos_theta;
d33_muld3(dir, d33_basis(basis, N), dir);
- *pdf = (blinn->exponent+2)/(2*PI)*pow(cos_theta, blinn->exponent+1);
d3_set(wh, dir);
+
+ if(pdf) *pdf = (blinn->exponent+2)/(2*PI)*pow(cos_theta, blinn->exponent+1);
}
static double
@@ -102,7 +99,7 @@ blinn_distribution_pdf(void* distrib, const double N[3], const double wh[3])
******************************************************************************/
const struct ssf_microfacet_distribution_type ssf_blinn_distribution = {
blinn_distribution_init,
- blinn_distribution_release,
+ NULL,
blinn_distribution_sample,
blinn_distribution_eval,
blinn_distribution_pdf,
diff --git a/src/ssf_bsdf.c b/src/ssf_bsdf.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -29,8 +29,6 @@ static INLINE int
check_bsdf_type(const struct ssf_bsdf_type* type)
{
return type
- && type->init
- && type->release
&& type->sample
&& type->eval
&& type->pdf
@@ -43,7 +41,7 @@ bsdf_release(ref_T* ref)
struct ssf_bsdf* bsdf = CONTAINER_OF(ref, struct ssf_bsdf, ref);
ASSERT(ref);
if(bsdf->data) {
- bsdf->type.release(bsdf->data);
+ if(bsdf->type.release) bsdf->type.release(bsdf->data);
MEM_RM(bsdf->allocator, bsdf->data);
}
MEM_RM(bsdf->allocator, bsdf);
@@ -76,15 +74,19 @@ ssf_bsdf_create
bsdf->allocator = mem_allocator;
bsdf->type = *type;
- bsdf->data = MEM_ALLOC_ALIGNED
- (bsdf->allocator, bsdf->type.sizeof_bsdf, bsdf->type.alignof_bsdf);
- if(!bsdf->data) {
- res = RES_MEM_ERR;
- goto error;
+ if(bsdf->type.sizeof_bsdf) {
+ bsdf->data = MEM_ALLOC_ALIGNED
+ (bsdf->allocator, bsdf->type.sizeof_bsdf, bsdf->type.alignof_bsdf);
+ if(!bsdf->data) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memset(bsdf->data, 0, bsdf->type.sizeof_bsdf);
+ if(bsdf->type.init) {
+ res = bsdf->type.init(mem_allocator, bsdf->data);
+ if(res != RES_OK) goto error;
+ }
}
- memset(bsdf->data, 0, bsdf->type.sizeof_bsdf);
- res = bsdf->type.init(mem_allocator, bsdf->data);
- if(res != RES_OK) goto error;
exit:
if(out_bsdf) *out_bsdf = bsdf;
@@ -121,11 +123,13 @@ ssf_bsdf_sample
const double N[3],
double wi[3],
int* type,
- double* pdf)
+ double* out_pdf)
{
- ASSERT(bsdf && rng && wo && N && wi && type && pdf);
+ double R;
+ ASSERT(bsdf && rng && wo && N && wi);
ASSERT(d3_is_normalized(wo) && d3_is_normalized(N));
- return bsdf->type.sample(bsdf->data, rng, wo, N, wi, type, pdf);
+ R = bsdf->type.sample(bsdf->data, rng, wo, N, wi, type, out_pdf);
+ return R;
}
double
diff --git a/src/ssf_bsdf_c.h b/src/ssf_bsdf_c.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/ssf_fresnel.c b/src/ssf_fresnel.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -27,8 +27,6 @@ static INLINE int
check_fresnel_type(const struct ssf_fresnel_type* type)
{
return type
- && type->init
- && type->release
&& type->eval
&& IS_POW2(type->alignof_fresnel);
}
@@ -39,7 +37,7 @@ fresnel_release(ref_T* ref)
struct ssf_fresnel* fresnel = CONTAINER_OF(ref, struct ssf_fresnel, ref);
ASSERT(ref);
if(fresnel->data) {
- fresnel->type.release(fresnel->data);
+ if(fresnel->type.release) fresnel->type.release(fresnel->data);
MEM_RM(fresnel->allocator, fresnel->data);
}
MEM_RM(fresnel->allocator, fresnel);
@@ -73,15 +71,19 @@ ssf_fresnel_create
fresnel->allocator = mem_allocator;
fresnel->type = *type;
- fresnel->data = MEM_ALLOC_ALIGNED(fresnel->allocator,
- fresnel->type.sizeof_fresnel, fresnel->type.alignof_fresnel);
- if(!fresnel->data) {
- res = RES_MEM_ERR;
- goto error;
+ if(fresnel->type.sizeof_fresnel) {
+ fresnel->data = MEM_ALLOC_ALIGNED(fresnel->allocator,
+ fresnel->type.sizeof_fresnel, fresnel->type.alignof_fresnel);
+ if(!fresnel->data) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memset(fresnel->data, 0, fresnel->type.sizeof_fresnel);
+ if(fresnel->type.init) {
+ res = fresnel->type.init(fresnel->allocator, fresnel->data);
+ if(res != RES_OK) goto error;
+ }
}
- memset(fresnel->data, 0, fresnel->type.sizeof_fresnel);
- res = fresnel->type.init(fresnel->allocator, fresnel->data);
- if(res != RES_OK) goto error;
exit:
if(out_fresnel) *out_fresnel = fresnel;
diff --git a/src/ssf_fresnel_c.h b/src/ssf_fresnel_c.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/ssf_fresnel_constant.c b/src/ssf_fresnel_constant.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/ssf_fresnel_dielectric_conductor.c b/src/ssf_fresnel_dielectric_conductor.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -27,25 +27,6 @@ struct fresnel_dielectric_conductor {
/*******************************************************************************
* Private functions
******************************************************************************/
-static res_T
-fresnel_dielectric_conductor_init
- (struct mem_allocator* allocator, void* fresnel)
-{
- struct fresnel_dielectric_conductor* fc = fresnel;
- (void)allocator;
- ASSERT(fresnel);
- fc->eta_i = 0.0;
- fc->eta_t = 0.0;
- fc->eta_k_t = 0.0;
- return RES_OK;
-}
-
-static void
-fresnel_dielectric_conductor_release(void* fresnel)
-{
- (void)fresnel;
-}
-
/* Code from
* https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ */
static double
@@ -85,8 +66,8 @@ fresnel_dielectric_conductor_eval(void* fresnel, const double cos_theta_i)
* Exported symbols
******************************************************************************/
const struct ssf_fresnel_type ssf_fresnel_dielectric_conductor = {
- fresnel_dielectric_conductor_init,
- fresnel_dielectric_conductor_release,
+ NULL,
+ NULL,
fresnel_dielectric_conductor_eval,
sizeof(struct fresnel_dielectric_conductor),
ALIGNOF(struct fresnel_dielectric_conductor)
@@ -100,7 +81,7 @@ ssf_fresnel_dielectric_conductor_setup
const double eta_k_t)
{
struct fresnel_dielectric_conductor* fc;
- if(!fresnel
+ if(!fresnel
|| !FRESNEL_TYPE_EQ(&fresnel->type, &ssf_fresnel_dielectric_conductor)) {
return RES_BAD_ARG;
}
diff --git a/src/ssf_fresnel_dielectric_dielectric.c b/src/ssf_fresnel_dielectric_dielectric.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -26,24 +26,6 @@ struct fresnel_dielectric_dielectric {
/*******************************************************************************
* Private functions
******************************************************************************/
-static res_T
-fresnel_dielectric_dielectric_init
- (struct mem_allocator* allocator, void* fresnel)
-{
- struct fresnel_dielectric_dielectric* fd = fresnel;
- (void)allocator;
- ASSERT(fresnel);
- fd->eta_i = 0.0;
- fd->eta_t = 0.0;
- return RES_OK;
-}
-
-static void
-fresnel_dielectric_dielectric_release(void* fresnel)
-{
- (void)fresnel;
-}
-
static double
fresnel_dielectric_dielectric_eval(void* fresnel, const double cos_theta_i)
{
@@ -81,8 +63,8 @@ fresnel_dielectric_dielectric_eval(void* fresnel, const double cos_theta_i)
* Exported symbols
******************************************************************************/
const struct ssf_fresnel_type ssf_fresnel_dielectric_dielectric = {
- fresnel_dielectric_dielectric_init,
- fresnel_dielectric_dielectric_release,
+ NULL,
+ NULL,
fresnel_dielectric_dielectric_eval,
sizeof(struct fresnel_dielectric_dielectric),
ALIGNOF(struct fresnel_dielectric_dielectric)
diff --git a/src/ssf_fresnel_no_op.c b/src/ssf_fresnel_no_op.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/ssf_lambertian_reflection.c b/src/ssf_lambertian_reflection.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -28,19 +28,6 @@ struct lambertian_reflection {
/*******************************************************************************
* Private functions
******************************************************************************/
-static res_T
-lambertian_reflection_init(struct mem_allocator* allocator, void* bsdf)
-{
- ASSERT(bsdf);
- (void)allocator;
- ((struct lambertian_reflection*)bsdf)->reflectivity = 0.0;
- return RES_OK;
-}
-
-static void
-lambertian_reflection_release(void* bsdf)
-{ (void)bsdf; }
-
static double
lambertian_reflection_eval
(void* data, const double wo[3], const double N[3], const double wi[3])
@@ -64,13 +51,13 @@ lambertian_reflection_sample
double* pdf)
{
double sample[3];
- ASSERT(data && rng && wo && N && wi && type && pdf);
+ ASSERT(data && rng && wo && N && wi);
ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_dot(wo, N) > 0);
(void)wo;
ssp_ran_hemisphere_cos(rng, N, sample, pdf);
d3_set(wi, sample);
- *type = SSF_REFLECTION | SSF_DIFFUSE;
+ if(type) *type = SSF_REFLECTION | SSF_DIFFUSE;
return ((struct lambertian_reflection*)data)->reflectivity;
}
@@ -90,8 +77,8 @@ lambertian_reflection_pdf
* Exorted symbols
******************************************************************************/
const struct ssf_bsdf_type ssf_lambertian_reflection = {
- lambertian_reflection_init,
- lambertian_reflection_release,
+ NULL,
+ NULL,
lambertian_reflection_sample,
lambertian_reflection_eval,
lambertian_reflection_pdf,
diff --git a/src/ssf_microfacet_distribution.c b/src/ssf_microfacet_distribution.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -30,8 +30,6 @@ check_microfacet_distribution_type
(const struct ssf_microfacet_distribution_type* type)
{
return type
- && type->init
- && type->release
&& type->sample
&& type->eval
&& type->pdf
@@ -45,7 +43,7 @@ microfacet_distribution_release(ref_T* ref)
CONTAINER_OF(ref, struct ssf_microfacet_distribution, ref);
ASSERT(ref);
if(distrib->data) {
- distrib->type.release(distrib->data);
+ if(distrib->type.release) distrib->type.release(distrib->data);
MEM_RM(distrib->allocator, distrib->data);
}
MEM_RM(distrib->allocator, distrib);
@@ -79,17 +77,21 @@ ssf_microfacet_distribution_create
distrib->allocator = mem_allocator;
distrib->type = *type;
- distrib->data = MEM_ALLOC_ALIGNED
- (distrib->allocator,
- distrib->type.sizeof_distribution,
- distrib->type.alignof_distribution);
- if(!distrib->data) {
- res = RES_MEM_ERR;
- goto error;
+ if(distrib->type.sizeof_distribution) {
+ distrib->data = MEM_ALLOC_ALIGNED
+ (distrib->allocator,
+ distrib->type.sizeof_distribution,
+ distrib->type.alignof_distribution);
+ if(!distrib->data) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memset(distrib->data, 0, distrib->type.sizeof_distribution);
+ if(distrib->type.init) {
+ res = distrib->type.init(mem_allocator, distrib->data);
+ if(res != RES_OK) goto error;
+ }
}
- memset(distrib->data, 0, distrib->type.sizeof_distribution);
- res = distrib->type.init(mem_allocator, distrib->data);
- if(res != RES_OK) goto error;
exit:
if(out_distrib) *out_distrib = distrib;
@@ -124,11 +126,11 @@ ssf_microfacet_distribution_sample
struct ssp_rng* rng,
const double N[3],
double wh[3],
- double* pdf)
+ double* out_pdf)
{
- ASSERT(distrib && rng && N && wh && pdf);
+ ASSERT(distrib && rng && N && wh);
ASSERT(d3_is_normalized(N));
- distrib->type.sample(distrib->data, rng, N, wh, pdf);
+ distrib->type.sample(distrib->data, rng, N, wh, out_pdf);
}
double
diff --git a/src/ssf_microfacet_distribution_c.h b/src/ssf_microfacet_distribution_c.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/ssf_microfacet_reflection.c b/src/ssf_microfacet_reflection.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -27,17 +27,6 @@ struct microfacet_reflection {
/*******************************************************************************
* Microfacet functions
******************************************************************************/
-static res_T
-microfacet_reflection_init(struct mem_allocator* allocator, void* data)
-{
- struct microfacet_reflection* bsdf = data;
- ASSERT(bsdf);
- (void)allocator;
- bsdf->fresnel = NULL;
- bsdf->distrib = NULL;
- return RES_OK;
-}
-
static void
microfacet_reflection_release(void* data)
{
@@ -94,14 +83,14 @@ microfacet_reflection_sample
double wh[3];
double pdf_wh;
double R;
- ASSERT(data && wo && N && wi && pdf && type);
+ ASSERT(data && wo && N && wi);
ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_dot(wo, N) > 0);
ASSERT(bsdf->distrib && bsdf->fresnel);
ssf_microfacet_distribution_sample(bsdf->distrib, rng, N, wh, &pdf_wh);
reflect(dir, wo, wh);
- *pdf = pdf_wh / (4.0*fabs(d3_dot(wo, N)));
- *type = SSF_REFLECTION | SSF_GLOSSY;
+ if(pdf) *pdf = pdf_wh / (4.0*fabs(d3_dot(wo, N)));
+ if(type) *type = SSF_REFLECTION | SSF_GLOSSY;
R = d3_dot(dir, N) > 0 ? ssf_fresnel_eval(bsdf->fresnel, d3_dot(dir, wh)) : 0;
d3_set(wi, dir);
return R;
@@ -116,9 +105,10 @@ microfacet_reflection_pdf
double pdf_wh;
ASSERT(data && wo && N && wi);
ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_is_normalized(wi));
- ASSERT(d3_dot(wo, N) > 0 && d3_dot(wi, N) > 0);
+ ASSERT(d3_dot(wo, N) > 0);
ASSERT(bsdf->distrib);
d3_normalize(wh, d3_add(wh, wi, wo));
+ if(d3_dot(wh, N) < 0) d3_minus(wh, wh);
pdf_wh = ssf_microfacet_distribution_pdf(bsdf->distrib, N, wh);
return pdf_wh / (4.0*fabs(d3_dot(wo, N)));
}
@@ -141,7 +131,7 @@ microfacet2_reflection_sample
double wh[3];
double p;
double troughput = 1;
- ASSERT(data && wo && N && wi && pdf && type);
+ ASSERT(data && wo && N && wi);
ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_dot(N, wo) > 0.0);
ASSERT(bsdf->distrib);
@@ -165,8 +155,8 @@ microfacet2_reflection_sample
reflect(dir, dir, wh);
}
- *pdf = NaN;
- *type = SSF_REFLECTION | SSF_GLOSSY;
+ if(pdf) *pdf = NaN;
+ if(type) *type = SSF_REFLECTION | SSF_GLOSSY;
d3_set(wi, dir);
return troughput;
}
@@ -191,7 +181,7 @@ microfacet2_reflection_pdf
* Exported symbols
******************************************************************************/
const struct ssf_bsdf_type ssf_microfacet_reflection = {
- microfacet_reflection_init,
+ NULL,
microfacet_reflection_release,
microfacet_reflection_sample,
microfacet_reflection_eval,
@@ -201,7 +191,7 @@ const struct ssf_bsdf_type ssf_microfacet_reflection = {
};
const struct ssf_bsdf_type ssf_microfacet2_reflection = {
- microfacet_reflection_init,
+ NULL,
microfacet_reflection_release,
microfacet2_reflection_sample,
microfacet2_reflection_eval,
diff --git a/src/ssf_optics.h b/src/ssf_optics.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/ssf_phase.c b/src/ssf_phase.c
@@ -0,0 +1,160 @@
+/* Copyright (C) 2016-2018 |Meso|Star> (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->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) {
+ if(phase->type.release) 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;
+
+ if(phase->type.sizeof_phase) {
+ 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);
+ if(phase->type.init) {
+ 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;
+}
+
+void
+ssf_phase_sample
+ (struct ssf_phase* phase,
+ struct ssp_rng* rng,
+ const double wo[3],
+ double wi[3],
+ double* out_pdf)
+{
+ ASSERT(phase && rng && wo && wi);
+ ASSERT(d3_is_normalized(wo));
+ phase->type.sample(phase->data, rng, wo, wi, out_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) 2016-2018 |Meso|Star> (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,113 @@
+/* Copyright (C) 2016-2018 |Meso|Star> (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 double
+hg_pdf
+ (void* data,
+ const double wo[3],
+ const double wi[3])
+{
+ double w[3];
+ const struct hg* hg = data;
+ ASSERT(data && wo && wi);
+ return ssp_ran_sphere_hg_pdf(d3_minus(w, wo), hg->g, wi);
+}
+
+/* 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 w[3];
+ double g;
+ double cos_theta;
+ double denom;
+ double val;
+ ASSERT(data && wo && wi);
+ ASSERT(d3_is_normalized(wo) && d3_is_normalized(wi));
+
+ g = hg->g;
+ /* By convention wo points outward the scattering point. Revert it to point
+ * inward the scattering point in order to compute the cosine of the
+ * scattering angle */
+ d3_minus(w, wo);
+ cos_theta = d3_dot(w, wi);
+ denom = 1 + g*g - 2*g*cos_theta;
+ ASSERT(denom != 0);
+ val = 1.0/(4.0*PI) * (1 - g*g) / (denom*sqrt(denom));
+ ASSERT(eq_eps(val, hg_pdf(data, wo, wi), 1.e-6));
+ return val;
+}
+
+static void
+hg_sample
+ (void* data,
+ struct ssp_rng* rng,
+ const double wo[3],
+ double wi[3],
+ double* pdf)
+{
+ const struct hg* hg = data;
+ double w[3];
+ ASSERT(data && wo && wi);
+
+ /* By convention wo points outward the scattering point. Revert it to point
+ * inward the scattering point as expected by the SSP library */
+ ssp_ran_sphere_hg(rng, d3_minus(w, wo), hg->g, wi, pdf);
+}
+
+/*******************************************************************************
+ * Exported symbols
+ ******************************************************************************/
+const struct ssf_phase_type ssf_phase_hg = {
+ hg_init,
+ NULL,
+ 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;
+}
+
diff --git a/src/ssf_phase_rayleigh.c b/src/ssf_phase_rayleigh.c
@@ -0,0 +1,111 @@
+/* Copyright (C) 2016-2018 |Meso|Star> (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/>. */
+
+#define _POSIX_C_SOURCE 200112L /* cbrt support */
+
+#include "ssf.h"
+#include "ssf_phase_c.h"
+
+#include <rsys/double3.h>
+#include <star/ssp.h>
+#include <math.h>
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static res_T
+rayleigh_init(struct mem_allocator* allocator, void* phase)
+{
+ (void)allocator, (void)phase;
+ return RES_OK;
+}
+
+static void
+rayleigh_release(void* phase)
+{ (void)phase; }
+
+
+/* Rayleigh(theta) = 3/(16*PI)*(1+cos(theta)^2) */
+static double
+rayleigh_eval(void* data, const double wo[3], const double wi[3])
+{
+ double cos_theta;
+ double w[3];
+ ASSERT(wo && wi);
+ ASSERT(d3_is_normalized(wo) && d3_is_normalized(wi));
+ (void)data;
+ /* By convention wo points outward the scattering point. Revert it to point
+ * inward the scattering point in order to compute the cosine of the
+ * scattering angle */
+ d3_minus(w, wo);
+ cos_theta = d3_dot(w, wi);
+ return 3.0/(16.0*PI)*(1.0+cos_theta*cos_theta);
+}
+
+static void
+rayleigh_sample
+ (void* data,
+ struct ssp_rng* rng,
+ const double wo[3],
+ double wi[3],
+ double* pdf)
+{
+ double frame[9];
+ double sample[3];
+ double w[3];
+ double phi, cos_theta, sin_theta;
+ double u;
+ double s;
+ ASSERT(rng && wo && wi);
+ (void)data;
+
+ phi = ssp_rng_uniform_double(rng, 0, 2*PI);
+
+ u = 4*ssp_rng_canonical(rng)-2;
+ s = cbrt(u + sqrt(1+u*u));
+ cos_theta = s - 1.0/s;
+ sin_theta = cos2sin(cos_theta);
+
+ sample[0] = cos(phi) * sin_theta;
+ sample[1] = sin(phi) * sin_theta;
+ sample[2] = cos_theta;
+
+ /* By convention wo points outward the scattering point. Revert it to point
+ * inward the scattering point */
+ d3_minus(w, wo);
+ d33_muld3(wi, d33_basis(frame, w), sample);
+
+ if(pdf) *pdf = rayleigh_eval(data, wo, wi);
+}
+
+static double
+rayleigh_pdf(void* data, const double wo[3], const double wi[3])
+{
+ return rayleigh_eval(data, wo, wi);
+}
+
+/*******************************************************************************
+ * Exported symbols
+ ******************************************************************************/
+const struct ssf_phase_type ssf_phase_rayleigh = {
+ rayleigh_init,
+ rayleigh_release,
+ rayleigh_sample,
+ rayleigh_eval,
+ rayleigh_pdf,
+ 0,
+ 1
+};
+
diff --git a/src/ssf_pillbox_distribution.c b/src/ssf_pillbox_distribution.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -82,7 +82,8 @@ pillbox_distribution_sample
dir[1] = sin(phi) * sin_theta;
dir[2] = cos_theta;
d33_muld3(wh, d33_basis(basis, N), dir);
- *pdf = cos_theta / (PI-PI*cos2_theta_max);
+
+ if(pdf) *pdf = cos_theta / (PI-PI*cos2_theta_max);
}
static double
diff --git a/src/ssf_specular_dielectric_dielectric_interface.c b/src/ssf_specular_dielectric_dielectric_interface.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -63,7 +63,7 @@ ssf_specular_dielectric_dielectric_interface_sample
double cos_wo_N;
double eta; /* Ratio of eta_i / eta_t */
double R;
- ASSERT(bsdf && rng && wi && N && wo && type && pdf);
+ ASSERT(bsdf && rng && wi && N && wo);
ASSERT(d3_is_normalized(wo) && d3_is_normalized(N));
ASSERT(d3_dot(wo, N) > -1.e-6);
(void)rng;
@@ -72,8 +72,8 @@ ssf_specular_dielectric_dielectric_interface_sample
refracted = refract(wt, wo, N, eta);
if(!refracted) { /* Total reflection */
reflect(wi, wo, N);
- *pdf = INF;
- *type = SSF_SPECULAR | SSF_REFLECTION;
+ if(pdf) *pdf = INF;
+ if(type) *type = SSF_SPECULAR | SSF_REFLECTION;
return 1;
}
fresnel = bsdf->fresnel;
@@ -82,15 +82,15 @@ ssf_specular_dielectric_dielectric_interface_sample
SSF(fresnel_dielectric_dielectric_setup(fresnel, bsdf->eta_i, bsdf->eta_t));
R = ssf_fresnel_eval(fresnel, cos_wo_N);
- *pdf = INF;
+ if(pdf) *pdf = INF;
/* Sample the output direction wrt R */
if(ssp_rng_canonical(rng) < R) {
reflect(wi, wo, N);
- *type = SSF_SPECULAR | SSF_REFLECTION;
+ if(type) *type = SSF_SPECULAR | SSF_REFLECTION;
} else {
d3_set(wi, refracted);
- *type = SSF_SPECULAR | SSF_TRANSMISSION;
+ if(type) *type = SSF_SPECULAR | SSF_TRANSMISSION;
}
return 1;
}
diff --git a/src/ssf_specular_reflection.c b/src/ssf_specular_reflection.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -26,14 +26,6 @@ struct specular_reflection {
/*******************************************************************************
* Private functions
******************************************************************************/
-static res_T
-specular_reflection_init(struct mem_allocator* allocator, void* bsdf)
-{
- ASSERT(bsdf);
- (void)allocator, (void)bsdf;
- return RES_OK;
-}
-
static void
specular_reflection_release(void* data)
{
@@ -55,14 +47,14 @@ specular_reflection_sample
struct specular_reflection* brdf = data;
double cos_wo_N;
double cos_wi_N;
- ASSERT(rng && wi && N && wo && type && pdf);
+ ASSERT(rng && wi && N && wo);
ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_dot(wo, N) > 0);
(void)rng;
/* Reflect the outgoing direction wo with respect to the surface normal N */
reflect(wi, wo, N);
- *pdf = INF;
- *type = SSF_REFLECTION | SSF_SPECULAR;
+ if(pdf) *pdf = INF;
+ if(type) *type = SSF_REFLECTION | SSF_SPECULAR;
cos_wi_N = cos_wo_N = d3_dot(wo, N);
return ssf_fresnel_eval(brdf->fresnel, cos_wi_N);
@@ -88,7 +80,7 @@ specular_reflection_pdf
* Exported symbols
******************************************************************************/
const struct ssf_bsdf_type ssf_specular_reflection = {
- specular_reflection_init,
+ NULL,
specular_reflection_release,
specular_reflection_sample,
specular_reflection_eval,
diff --git a/src/ssf_thin_specular_dielectric.c b/src/ssf_thin_specular_dielectric.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -66,7 +66,7 @@ thin_specular_dielectric_sample
double R, T;
double rho1, rho2, rho2_sqr;
double tau, tau_sqr;
- ASSERT(bsdf && rng && wi && N && wo && type && pdf);
+ ASSERT(bsdf && rng && wi && N && wo);
ASSERT(d3_is_normalized(wo) && d3_is_normalized(N));
ASSERT(d3_dot(wo, N) > -1.e-6);
(void)rng;
@@ -74,8 +74,8 @@ thin_specular_dielectric_sample
eta = bsdf->eta_i / bsdf->eta_t;
if(!refract(wt, wo, N, eta)) { /* Total reflection */
reflect(wi, wo, N);
- *pdf = INF;
- *type = SSF_SPECULAR | SSF_REFLECTION;
+ if(pdf) *pdf = INF;
+ if(type) *type = SSF_SPECULAR | SSF_REFLECTION;
return 1;
}
fresnel = bsdf->fresnel;
@@ -103,15 +103,15 @@ thin_specular_dielectric_sample
}
#endif
- *pdf = INF;
+ if(pdf) *pdf = INF;
/* Importance sample the BTDF wrt R */
if(ssp_rng_uniform_double(rng, 0, R + T) < R) {
reflect(wi, wo, N);
- *type = SSF_SPECULAR | SSF_REFLECTION;
+ if(type) *type = SSF_SPECULAR | SSF_REFLECTION;
} else { /* Sample the transmissive part */
d3_minus(wi, wo);
- *type = SSF_SPECULAR | SSF_TRANSMISSION;
+ if(type) *type = SSF_SPECULAR | SSF_TRANSMISSION;
}
if(bsdf->absorption == 0) {
/* avoid numerical loss of energy if no absorption */
diff --git a/src/test_ssf_beckmann_distribution.c b/src/test_ssf_beckmann_distribution.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/test_ssf_blinn_distribution.c b/src/test_ssf_blinn_distribution.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/test_ssf_bsdf.c b/src/test_ssf_bsdf.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -67,8 +67,8 @@ bsdf_sample
CHK(d3_eq(BxDF->wo, wo) == 1);
CHK(d3_eq(BxDF->N, N) == 1);
d3(wi, 1.0, 2.0, 3.0);
- *type = 314;
- *pdf = 4;
+ if(type) *type = 314;
+ if(pdf) *pdf = 4;
return BxDF->reflectivity;
}
@@ -114,6 +114,7 @@ main(int argc, char** argv)
struct bsdf* data;
struct ssf_bsdf* bsdf;
struct ssf_bsdf_type type = SSF_BXDF_TYPE_NULL;
+ struct ssf_bsdf_type type2 = bsdf_dummy;
double wo[3];
double N[3];
double wi[4];
@@ -156,14 +157,14 @@ main(int argc, char** argv)
CHK(ssf_bsdf_ref_put(bsdf) == RES_OK);
CHK(bsdf_is_init == 0);
- type.init = NULL;
- CHK(ssf_bsdf_create(&allocator, &type, &bsdf) == RES_BAD_ARG);
- CHK(bsdf_is_init == 0);
- type.init = bsdf_init;
- type.release = NULL;
- CHK(ssf_bsdf_create(&allocator, &type, &bsdf) == RES_BAD_ARG);
- CHK(bsdf_is_init == 0);
- type.release = bsdf_release;
+ type2.init = NULL;
+ CHK(ssf_bsdf_create(&allocator, &type2, &bsdf) == RES_OK);
+ CHK(ssf_bsdf_ref_put(bsdf) == RES_OK);
+ type2.init = bsdf_dummy.init;
+ type2.release = NULL;
+ CHK(ssf_bsdf_create(&allocator, &type2, &bsdf) == RES_OK);
+ CHK(ssf_bsdf_ref_put(bsdf) == RES_OK);
+
type.sample = NULL;
CHK(ssf_bsdf_create(&allocator, &type, &bsdf) == RES_BAD_ARG);
CHK(bsdf_is_init == 0);
@@ -189,10 +190,12 @@ main(int argc, char** argv)
data->rng = rng;
data->reflectivity = 0.1234;
- CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, &i, &pdf) == 0.1234);
- CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, &i, &pdf) == 0.1234);
+ i = 0, pdf = 0;
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, &i, NULL) == 0.1234);
CHK(i == 314);
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, &pdf) == 0.1234);
CHK(pdf == 4);
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, NULL) == 0.1234);
data->reflectivity = 0.314;
CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, &i, &pdf) == 0.314);
diff --git a/src/test_ssf_fresnel.c b/src/test_ssf_fresnel.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -60,6 +60,7 @@ main(int argc, char** argv)
struct fresnel* data;
struct ssf_fresnel* fresnel;
struct ssf_fresnel_type type = SSF_FRESNEL_TYPE_NULL;
+ struct ssf_fresnel_type type2 = fresnel_dummy;
(void)argc, (void)argv;
mem_init_proxy_allocator(&allocator, &mem_default_allocator);
@@ -96,14 +97,13 @@ main(int argc, char** argv)
CHK(ssf_fresnel_ref_put(fresnel) == RES_OK);
CHK(fresnel_is_init == 0);
- type.init = NULL;
- CHK(ssf_fresnel_create(&allocator, &type, &fresnel) == RES_BAD_ARG);
- CHK(fresnel_is_init == 0);
- type.init = fresnel_init;
- type.release = NULL;
- CHK(ssf_fresnel_create(&allocator, &type, &fresnel) == RES_BAD_ARG);
- CHK(fresnel_is_init == 0);
- type.release = fresnel_release;
+ type2.init = NULL;
+ CHK(ssf_fresnel_create(&allocator, &type2, &fresnel) == RES_OK);
+ CHK(ssf_fresnel_ref_put(fresnel) == RES_OK);
+ type2.init = fresnel_dummy.init;
+ CHK(ssf_fresnel_create(&allocator, &type2, &fresnel) == RES_OK);
+ CHK(ssf_fresnel_ref_put(fresnel) == RES_OK);
+
type.eval = NULL;
CHK(ssf_fresnel_create(&allocator, &type, &fresnel) == RES_BAD_ARG);
CHK(fresnel_is_init == 0);
diff --git a/src/test_ssf_fresnel_constant.c b/src/test_ssf_fresnel_constant.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/test_ssf_fresnel_dielectric_conductor.c b/src/test_ssf_fresnel_dielectric_conductor.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/test_ssf_fresnel_dielectric_dielectric.c b/src/test_ssf_fresnel_dielectric_dielectric.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/test_ssf_fresnel_no_op.c b/src/test_ssf_fresnel_no_op.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/test_ssf_lambertian_reflection.c b/src/test_ssf_lambertian_reflection.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -66,6 +66,13 @@ main(int argc, char** argv)
CHK(eq_eps(pdf, d3_dot(wi, N)/PI, 1.e-6) == 1);
CHK(type == (SSF_DIFFUSE|SSF_REFLECTION));
+ pdf = 0, type = 0;
+ CHK(ssf_bsdf_sample(brdf, rng, wo, N, wi, NULL, &pdf) == R);
+ CHK(eq_eps(pdf, d3_dot(wi, N)/PI, 1.e-6) == 1);
+ CHK(ssf_bsdf_sample(brdf, rng, wo, N, wi, &type, NULL) == R);
+ CHK(type == (SSF_DIFFUSE|SSF_REFLECTION));
+ CHK(ssf_bsdf_sample(brdf, rng, wo, N, wi, NULL, NULL) == R);
+
d3_normalize(wo, d3(wo, 1.0, 0.0, 1.0));
N[0] = ssp_rng_uniform_double(rng, -1, 1);
N[1] = ssp_rng_uniform_double(rng, -1, 1);
diff --git a/src/test_ssf_microfacet_distribution.c b/src/test_ssf_microfacet_distribution.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -60,11 +60,10 @@ ufacet_sample
CHK(ufacet != NULL);
CHK(N != NULL);
CHK(wh != NULL);
- CHK(pdf != NULL);
CHK(ufacet->rng == rng);
CHK(d3_eq(ufacet->N, N) == 1);
d3_normalize(wh, d3(wh, 1.0, 2.0, 3.0));
- *pdf = ufacet->pdf;
+ if(pdf) *pdf = ufacet->pdf;
}
static double
@@ -103,6 +102,7 @@ main(int argc, char** argv)
struct ufacet* data;
struct ssf_microfacet_distribution_type type =
SSF_MICROFACET_DISTRIBUTION_TYPE_NULL;
+ struct ssf_microfacet_distribution_type type2 = microfacet_dummy;
struct ssf_microfacet_distribution* distrib;
struct ssp_rng* rng;
double N[3], wh[3], pdf;
@@ -144,12 +144,14 @@ main(int argc, char** argv)
CHK(ssf_microfacet_distribution_ref_put(distrib) == RES_OK);
CHK(ufacet_is_init == 0);
- type.init = NULL;
- CHK(CREATE(&allocator, &type, &distrib) == RES_BAD_ARG);
- type.init = ufacet_init;
- type.release = NULL;
- CHK(CREATE(&allocator, &type, &distrib) == RES_BAD_ARG);
- type.release = ufacet_release;
+ type2.init = NULL;
+ CHK(CREATE(&allocator, &type2, &distrib) == RES_OK);
+ CHK(ssf_microfacet_distribution_ref_put(distrib) == RES_OK);
+ type2.init = microfacet_dummy.init;
+ type2.release = NULL;
+ CHK(CREATE(&allocator, &type2, &distrib) == RES_OK);
+ CHK(ssf_microfacet_distribution_ref_put(distrib) == RES_OK);
+
type.sample = NULL;
CHK(CREATE(&allocator, &type, &distrib) == RES_BAD_ARG);
type.sample = ufacet_sample;
@@ -179,6 +181,7 @@ main(int argc, char** argv)
data->pdf = 0.1234;
data->value = 0.43;
+ ssf_microfacet_distribution_sample(distrib, rng, N, wh, NULL);
ssf_microfacet_distribution_sample(distrib, rng, N, wh, &pdf);
CHK(d3_eq(wh, data->wh) == 1);
CHK(pdf == data->pdf);
diff --git a/src/test_ssf_microfacet_reflection.c b/src/test_ssf_microfacet_reflection.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -30,7 +30,11 @@ main(int argc, char** argv)
struct ssp_rng* rng;
double N[3];
double wo[3];
- size_t i, NSTEPS=100000;
+ double wi[3];
+ double pdf;
+ double R;
+ size_t i, NSTEPS=10000;
+ int type;
(void)argc, (void)argv;
mem_init_proxy_allocator(&allocator, &mem_default_allocator);
@@ -56,11 +60,46 @@ main(int argc, char** argv)
ssp_ran_sphere_uniform(rng, N, NULL);
ssp_ran_hemisphere_cos(rng, N, wo, NULL);
FOR_EACH(i, 0, NSTEPS) {
- double wi[3], pdf;
- int type;
CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, &pdf) == 1);
CHK(type == (SSF_GLOSSY | SSF_REFLECTION));
+ CHK(IS_NaN(pdf));
CHK(d3_dot(wi, N) > 0);
+
+ pdf = 0, type = 0;
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, &pdf) == 1);
+ CHK(IS_NaN(pdf));
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, NULL) == 1);
+ CHK(type == (SSF_GLOSSY | SSF_REFLECTION));
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, NULL) == 1);
+ }
+
+ CHK(ssf_bsdf_ref_put(bsdf) == RES_OK);
+ CHK(ssf_bsdf_create(&allocator, &ssf_microfacet_reflection, &bsdf) == RES_OK);
+
+ CHK(ssf_microfacet_reflection_setup(NULL, NULL, NULL) == RES_BAD_ARG);
+ CHK(ssf_microfacet_reflection_setup(bsdf, NULL, NULL) == RES_BAD_ARG);
+ CHK(ssf_microfacet_reflection_setup(NULL, F, NULL) == RES_BAD_ARG);
+ CHK(ssf_microfacet_reflection_setup(bsdf, F, NULL) == RES_BAD_ARG);
+ CHK(ssf_microfacet_reflection_setup(NULL, NULL, D) == RES_BAD_ARG);
+ CHK(ssf_microfacet_reflection_setup(bsdf, NULL, D) == RES_BAD_ARG);
+ CHK(ssf_microfacet_reflection_setup(NULL, F, D) == RES_BAD_ARG);
+ CHK(ssf_microfacet_reflection_setup(bsdf, F, D) == RES_OK);
+
+ FOR_EACH(i, 0, NSTEPS) {
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, &pdf);
+ CHK(R == (d3_dot(wi, N) > 0 ? 1 : 0));
+ CHK(type == (SSF_GLOSSY | SSF_REFLECTION));
+ CHK(eq_eps(pdf, ssf_bsdf_pdf(bsdf, wo, N, wi), 1.e-6));
+
+ pdf = 0, type = 0;
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, &pdf);
+ CHK(R == (d3_dot(wi, N) > 0 ? 1 : 0));
+ CHK(eq_eps(pdf, ssf_bsdf_pdf(bsdf, wo, N, wi), 1.e-6));
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, NULL);
+ CHK(R == (d3_dot(wi, N) > 0 ? 1 : 0));
+ CHK(type == (SSF_GLOSSY | SSF_REFLECTION));
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, NULL);
+ CHK(R == (d3_dot(wi, N) > 0 ? 1 : 0));
}
CHK(ssf_bsdf_ref_put(bsdf) == RES_OK);
diff --git a/src/test_ssf_phase.c b/src/test_ssf_phase.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 2016-2018 |Meso|Star> (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 "test_ssf_utils.h"
+
+static int phase_is_init = 0;
+
+struct ALIGN(64) phase {
+ uint32_t id;
+ struct ssp_rng* rng;
+ double wi[3];
+ double wo[3];
+ double pdf;
+ double value;
+};
+
+static res_T
+phase_init(struct mem_allocator* allocator, void* phase)
+{
+ CHK(allocator != NULL);
+ CHK(phase != NULL);
+ CHK(IS_ALIGNED(phase, 64) == 1);
+ ((struct phase*)phase)->id = 0xDECAFBAD;
+ phase_is_init = 1;
+ return RES_OK;
+}
+
+static void
+phase_release(void* phase)
+{
+ CHK(phase != NULL);
+ CHK(((struct phase*)phase)->id == 0xDECAFBAD);
+ phase_is_init = 0;
+}
+
+static void
+phase_sample
+ (void* data,
+ struct ssp_rng* rng,
+ const double wo[3],
+ double wi[3],
+ double* pdf)
+{
+ struct phase* phase = data;
+ CHK(phase != NULL);
+ CHK(phase->rng == rng);
+ CHK(d3_eq(phase->wo, wo) == 1);
+ d3(wi, 1.0, 2.0, 3.0);
+ if(pdf) *pdf = phase->pdf;
+}
+
+static double
+phase_eval
+ (void* data,
+ const double wo[3],
+ const double wi[3])
+{
+ struct phase* phase = data;
+ CHK(phase != NULL);
+ CHK(wi != NULL);
+ CHK(wo != NULL);
+ CHK(d3_eq(phase->wo, wo) == 1);
+ CHK(d3_eq(phase->wi, wi) == 1);
+ return phase->value;
+}
+
+static double
+phase_pdf
+ (void* data,
+ const double wo[3],
+ const double wi[3])
+{
+ struct phase* phase = data;
+ CHK(phase != NULL);
+ CHK(wi != NULL);
+ CHK(wo != NULL);
+ CHK(d3_eq(phase->wo, wo) == 1);
+ CHK(d3_eq(phase->wi, wi) == 1);
+ return phase->pdf;
+}
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct ssp_rng* rng;
+ struct ssf_phase* phase = NULL;
+ struct ssf_phase_type type = SSF_PHASE_TYPE_NULL;
+ struct ssf_phase_type type2 = phase_dummy;
+ struct phase* data = NULL;
+ double wi[3];
+ double pdf;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+ CHK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng) == RES_OK);
+
+ type.init = phase_init;
+ type.release = phase_release;
+ type.sample = phase_sample;
+ type.eval = phase_eval;
+ type.pdf = phase_pdf;
+ type.sizeof_phase = sizeof(struct phase);
+ type.alignof_phase = ALIGNOF(struct phase);
+
+ CHK(ssf_phase_create(NULL, NULL, NULL) == RES_BAD_ARG);
+ CHK(ssf_phase_create(&allocator, NULL, NULL) == RES_BAD_ARG);
+ CHK(ssf_phase_create(NULL, &type, NULL) == RES_BAD_ARG);
+ CHK(ssf_phase_create(&allocator, &type, NULL) == RES_BAD_ARG);
+ CHK(ssf_phase_create(NULL, NULL, &phase) == RES_BAD_ARG);
+ CHK(ssf_phase_create(&allocator, NULL, &phase) == RES_BAD_ARG);
+
+ CHK(phase_is_init == 0);
+ CHK(ssf_phase_create(NULL, &type, &phase) == RES_OK);
+ CHK(phase_is_init == 1);
+
+ CHK(ssf_phase_ref_get(NULL) == RES_BAD_ARG);
+ CHK(ssf_phase_ref_get(phase) == RES_OK);
+ CHK(ssf_phase_ref_put(NULL) == RES_BAD_ARG);
+ CHK(ssf_phase_ref_put(phase) == RES_OK);
+ CHK(phase_is_init == 1);
+ CHK(ssf_phase_ref_put(phase) == RES_OK);
+ CHK(phase_is_init == 0);
+
+ type2.init = NULL;
+ CHK(ssf_phase_create(&allocator, &type2, &phase) == RES_OK);
+ CHK(ssf_phase_ref_put(phase) == RES_OK);
+ type2.init = phase_dummy.init;
+ type2.release = NULL;
+ CHK(ssf_phase_create(&allocator, &type2, &phase) == RES_OK);
+ CHK(ssf_phase_ref_put(phase) == RES_OK);
+
+ type.sample = NULL;
+ CHK(ssf_phase_create(&allocator, &type, &phase) == RES_BAD_ARG);
+ CHK(phase_is_init == 0);
+ type.sample = phase_sample;
+ type.alignof_phase = 3;
+ CHK(ssf_phase_create(&allocator, &type, &phase) == RES_BAD_ARG);
+ CHK(phase_is_init == 0);
+ type.alignof_phase = ALIGNOF(struct phase);
+ CHK(ssf_phase_create(&allocator, &type, &phase) == RES_OK);
+ CHK(phase_is_init == 1);
+
+ CHK(ssf_phase_get_data(NULL, NULL) == RES_BAD_ARG);
+ CHK(ssf_phase_get_data(phase, NULL) == RES_BAD_ARG);
+ CHK(ssf_phase_get_data(NULL, (void**)&data) == RES_BAD_ARG);
+ CHK(ssf_phase_get_data(phase, (void**)&data) == RES_OK);
+
+ CHK(data->id == 0xDECAFBAD);
+
+ d3_normalize(data->wo, d3(data->wo, -1, -1, 0));
+ data->rng = rng;
+ data->value = 0.1234;
+ data->pdf = 4;
+
+ pdf = 0;
+ ssf_phase_sample(phase, rng, data->wo, wi, NULL);
+ ssf_phase_sample(phase, rng, data->wo, wi, &pdf);
+ CHK(pdf == 4);
+
+ d3_normalize(data->wi, wi);
+ data->value = 0.4567;
+ CHK(ssf_phase_eval(phase, data->wo, data->wi) == data->value);
+ data->pdf = 0.890;
+ CHK(ssf_phase_pdf(phase, data->wo, data->wi) == data->pdf);
+
+ CHK(ssf_phase_ref_put(phase) == RES_OK);
+
+ CHK(ssp_rng_ref_put(rng) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
+
diff --git a/src/test_ssf_phase_hg.c b/src/test_ssf_phase_hg.c
@@ -0,0 +1,98 @@
+/* Copyright (C) 2016-2018 |Meso|Star> (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 "test_ssf_utils.h"
+
+#define NG 10
+#define NSAMPS 10000
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct ssf_phase* phase;
+ struct ssf_phase* dummy;
+ struct ssp_rng* rng;
+ double wo[3];
+ double wi[3];
+ size_t i;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+ CHK(ssp_rng_create(&allocator, &ssp_rng_mt19937_64, &rng) == RES_OK);
+
+ CHK(ssf_phase_create(&allocator, &ssf_phase_hg, &phase) == RES_OK);
+ CHK(ssf_phase_create(&allocator, &phase_dummy, &dummy) == RES_OK);
+
+ CHK(ssf_phase_hg_setup(NULL, -2) == RES_BAD_ARG);
+ CHK(ssf_phase_hg_setup(phase, -2) == RES_BAD_ARG);
+ CHK(ssf_phase_hg_setup(NULL, -1) == RES_BAD_ARG);
+ CHK(ssf_phase_hg_setup(phase, -1) == RES_OK);
+ CHK(ssf_phase_hg_setup(phase, 0) == RES_OK);
+ CHK(ssf_phase_hg_setup(phase, 1) == RES_OK);
+ CHK(ssf_phase_hg_setup(dummy, 1) == RES_BAD_ARG);
+
+ FOR_EACH(i, 0, NG) {
+ const double g = ssp_rng_uniform_double(rng, -1, +1);
+ double sum_cos = 0;
+ double sum_cos_sqr = 0;
+ double E, V, SE;
+ size_t isamp;
+
+ CHK(ssf_phase_hg_setup(phase, g) == RES_OK);
+
+ ssp_ran_sphere_uniform(rng, wo, NULL);
+ FOR_EACH(isamp, 0, NSAMPS) {
+ double w[3];
+ double weight;
+ double pdf;
+ double r;
+ double ref;
+ ssf_phase_sample(phase, rng, wo, wi, &pdf);
+ CHK(d3_is_normalized(wi));
+ CHK(eq_eps(ssf_phase_pdf(phase, wo, wi), pdf, 1.e-6));
+ d3_minus(w, wo); /* Match the convention */
+ weight = d3_dot(w, wi);
+ sum_cos += weight;
+ sum_cos_sqr += weight*weight;
+
+ /* HG(theta) = 1/(4*PI) * (1 - g^2) / (1 + g^2 - 2*g*cos(theta))^3/2 */
+ r = ssf_phase_eval(phase, wo, wi);
+ ref = 1/(4*PI) * (1-g*g) / pow(1+g*g-2*g*d3_dot(w,wi), 1.5);
+ CHK(eq_eps(r, ref, 1.e-6));
+ CHK(eq_eps(r, pdf, 1.e-6));
+ }
+ /* On average cos(-wo,wi) should be g */
+ E = sum_cos / NSAMPS;
+ V = sum_cos_sqr / NSAMPS - E*E;
+ SE = sqrt(V/NSAMPS);
+ CHK(eq_eps(E, g, 3*SE));
+ }
+
+ ssp_ran_sphere_uniform(rng, wo, NULL);
+ ssf_phase_sample(phase, rng, wo, wi, NULL);
+ CHK(d3_is_normalized(wi));
+
+ CHK(ssf_phase_ref_put(phase) == RES_OK);
+ CHK(ssf_phase_ref_put(dummy) == RES_OK);
+ CHK(ssp_rng_ref_put(rng) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
+
diff --git a/src/test_ssf_phase_rayleigh.c b/src/test_ssf_phase_rayleigh.c
@@ -0,0 +1,85 @@
+/* Copyright (C) 2016-2018 |Meso|Star> (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 "test_ssf_utils.h"
+
+#define NSAMPS 10000
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct ssf_phase* phase;
+ struct ssp_rng* rng;
+ double wo[3];
+ double wi[3];
+ double sum_cos = 0;
+ double sum_cos_sqr = 0;
+ double sum_cos_sqr2 = 0;
+ double E, V, SE;
+ size_t i;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+ CHK(ssp_rng_create(&allocator, &ssp_rng_mt19937_64, &rng) == RES_OK);
+
+ CHK(ssf_phase_create(&allocator, &ssf_phase_rayleigh, &phase) == RES_OK);
+
+ ssp_ran_sphere_uniform(rng, wo, NULL);
+ FOR_EACH(i, 0, NSAMPS) {
+ double w[3];
+ double weight;
+ double pdf;
+ double ref;
+ double r;
+ ssf_phase_sample(phase, rng, wo, wi, &pdf);
+ CHK(d3_is_normalized(wi));
+ CHK(eq_eps(ssf_phase_pdf(phase, wo, wi), pdf, 1.e-6f));
+ d3_minus(w, wo);
+ weight = d3_dot(wo, wi);
+ sum_cos += weight;
+ sum_cos_sqr += weight*weight;
+ sum_cos_sqr2 += weight*weight*weight*weight;
+
+ r = ssf_phase_eval(phase, wo, wi);
+ ref = 3.0/(16.0*PI)*(1.0+weight*weight);
+ CHK(eq_eps(r, ref, 1.e-6));
+ CHK(eq_eps(r, pdf, 1.e-6));
+ }
+
+ /* On average cos(-wo,wi) should be 0 */
+ E = sum_cos / NSAMPS;
+ V = sum_cos_sqr / NSAMPS - E*E;
+ SE = sqrt(V/NSAMPS);
+ CHK(eq_eps(E, 0, SE*3));
+
+ /* On average cos(-wo,wi)^2 should be 2/5 */
+ E = sum_cos_sqr / NSAMPS;
+ V = sum_cos_sqr2 / NSAMPS - E*E;
+ SE = sqrt(V/NSAMPS);
+ CHK(eq_eps(E, 2.0/5.0, SE*3));
+
+ ssf_phase_sample(phase, rng, wo, wi, NULL);
+ CHK(d3_is_normalized(wi));
+
+ CHK(ssf_phase_ref_put(phase) == RES_OK);
+ CHK(ssp_rng_ref_put(rng) == RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
diff --git a/src/test_ssf_pillbox_distribution.c b/src/test_ssf_pillbox_distribution.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
diff --git a/src/test_ssf_specular_dielectric_dielectric_reflection.c b/src/test_ssf_specular_dielectric_dielectric_reflection.c
@@ -0,0 +1,138 @@
+/* Copyright (C) 2016-2018 |Meso|Star> (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_optics.h"
+#include "test_ssf_utils.h"
+
+#include <rsys/double3.h>
+
+int
+main(int argc, char** argv)
+{
+ const size_t NSTEPS = 10000;
+ struct mem_allocator allocator;
+ struct ssp_rng* rng;
+ struct ssf_bsdf* bsdf;
+ struct ssf_bsdf* dummy;
+ const double eta_i = 1.00027;
+ const double eta_t = 1.5;
+ const double eta = eta_i/eta_t;
+ double N[3];
+ double wo[3];
+ double wi[3];
+ double tmp[3];
+ double R;
+ double pdf;
+ double sum_t, sum_t2, t;
+ double sum_r, sum_r2, r;
+ size_t i;
+ int type;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+ CHK(ssp_rng_create(&allocator, &ssp_rng_mt19937_64, &rng) == RES_OK);
+ CHK(ssf_bsdf_create(&allocator,
+ &ssf_specular_dielectric_dielectric_interface, &bsdf) == RES_OK);
+ CHK(ssf_bsdf_create(&allocator, &bsdf_dummy, &dummy) == RES_OK);
+
+ #define SETUP ssf_specular_dielectric_dielectric_interface_setup
+ CHK(SETUP(NULL, 0, 0) == RES_BAD_ARG);
+ CHK(SETUP(bsdf, 0, 0) == RES_BAD_ARG);
+ CHK(SETUP(NULL, 1, 0) == RES_BAD_ARG);
+ CHK(SETUP(bsdf, 1, 0) == RES_BAD_ARG);
+ CHK(SETUP(NULL, 0, 1) == RES_BAD_ARG);
+ CHK(SETUP(bsdf, 0, 1) == RES_BAD_ARG);
+ CHK(SETUP(NULL, 1, 1) == RES_BAD_ARG);
+ CHK(SETUP(bsdf, 1, 1) == RES_OK);
+ CHK(SETUP(dummy, 1, 1) == RES_BAD_ARG);
+ CHK(SETUP(bsdf, 1.00027, 1.5) == RES_OK);
+ #undef SETUP
+
+ d3(N, 0, 1, 0);
+
+ /* Test transmission */
+ d3(wo, 0, 1, 0);
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, &pdf);
+ CHK(R != 0);
+ CHK(IS_INF(pdf));
+ CHK(d3_eq_eps(wi, d3(tmp,0,-1,0), 1.e-6));
+ CHK(type == (SSF_SPECULAR | SSF_TRANSMISSION));
+
+ /* Test reflection */
+ d3(wo, 1, 0, 0);
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, &pdf);
+ CHK(R != 0);
+ CHK(IS_INF(pdf));
+ CHK(d3_eq_eps(wi, d3(tmp,-1,0,0), 1.e-6));
+ CHK(type == (SSF_SPECULAR | SSF_REFLECTION));
+
+ d3_normalize(wo, d3(wo, 1, 0.0000001, 0));
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, &pdf);
+ CHK(R == 1);
+ CHK(IS_INF(pdf));
+ CHK(d3_eq_eps(wi, d3(tmp, -wo[0], wo[1], 0), 1.e-6) == 1);
+ CHK(type == (SSF_SPECULAR | SSF_REFLECTION));
+
+ pdf = 0, type = 0;
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, &pdf) == R);
+ CHK(IS_INF(pdf));
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, NULL) == R);
+ CHK(type == (SSF_SPECULAR | SSF_REFLECTION));
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, NULL) == R);
+
+ /* Check energy conservation */
+ sum_t = sum_t2 = 0;
+ sum_r = sum_r2 = 0;
+ FOR_EACH(i, 0, NSTEPS) {
+ ssp_ran_hemisphere_cos(rng, wo, N, NULL);
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, NULL);
+ if(type & SSF_TRANSMISSION) {
+ sum_t += R;
+ sum_t2 += R*R;
+ } else {
+ sum_r += R;
+ sum_r2 += R*R;
+ }
+ }
+
+ t = sum_t / (double)NSTEPS;
+ r = sum_r / (double)NSTEPS;
+ CHK(t > 0);
+ CHK(r > 0);
+ CHK(eq_eps(t + r, 1, 1.e-6));
+
+ FOR_EACH(i, 0, NSTEPS) {
+ d3_normalize(wo, d3(wo,1,1,0));
+ R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, &pdf);
+ CHK(R != 0);
+ CHK(type & SSF_SPECULAR);
+ CHK(IS_INF(pdf));
+ if(d3_eq_eps(wi, refract(tmp, wo, N, eta), 1.e-6)) {
+ CHK(type & SSF_TRANSMISSION);
+ } else {
+ CHK(d3_eq_eps(wi, reflect(tmp, wo, N), 1.e-6));
+ CHK(type & SSF_REFLECTION);
+ }
+ CHK(ssf_bsdf_pdf(bsdf, wo, N, wi) == 0);
+ CHK(ssf_bsdf_eval(bsdf, wo, N, wi) == 0);
+ }
+
+ CHK(ssp_rng_ref_put(rng) == RES_OK);
+ CHK(ssf_bsdf_ref_put(bsdf) == RES_OK);
+ CHK(ssf_bsdf_ref_put(dummy) == RES_OK);
+
+ return 0;
+}
diff --git a/src/test_ssf_specular_reflection.c b/src/test_ssf_specular_reflection.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -56,6 +56,13 @@ main(int argc, char** argv)
CHK(IS_INF(pdf) == 1);
CHK(type == (SSF_SPECULAR|SSF_REFLECTION));
+ pdf = 0, type = 0;
+ CHK(ssf_bsdf_sample(brdf, rng, wo, N, wi, NULL, &pdf) == R);
+ CHK(IS_INF(pdf));
+ CHK(ssf_bsdf_sample(brdf, rng, wo, N, wi, &type, NULL) == R);
+ CHK(type == (SSF_SPECULAR|SSF_REFLECTION));
+ CHK(ssf_bsdf_sample(brdf, rng, wo, N, wi, NULL, NULL) == R);
+
d3_normalize(wo, d3(wo, 1.0, 1.0, 0.0));
CHK(ssf_specular_reflection_setup(brdf, fresnel) == RES_OK);
R = ssf_bsdf_sample(brdf, rng, wo, N, wi, &type, &pdf);
diff --git a/src/test_ssf_specular_transmission.c b/src/test_ssf_specular_transmission.c
@@ -1,103 +0,0 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (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_optics.h"
-#include "test_ssf_utils.h"
-
-#include <rsys/double3.h>
-
-int
-main(int argc, char** argv)
-{
- struct mem_allocator allocator;
- struct ssp_rng* rng;
- struct ssf_bxdf* btdf;
- struct ssf_bxdf* dummy;
- struct ssf_fresnel* fresnel;
- double wo[3], wi[3], tmp[3];
- double N[3];
- double pdf;
- double R;
- int type;
- (void)argc, (void)argv;
-
- mem_init_proxy_allocator(&allocator, &mem_default_allocator);
- CHK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng) == RES_OK);
- CHK(ssf_bxdf_create(&allocator, &ssf_specular_transmission, &btdf) == RES_OK);
- CHK(ssf_bxdf_create(&allocator, &bxdf_dummy, &dummy) == RES_OK);
- CHECK(ssf_fresnel_create
- (&allocator, &ssf_fresnel_dielectric_dielectric, &fresnel), RES_OK);
- CHK(ssf_fresnel_dielectric_dielectric_setup(fresnel, 1.00027, 1.5) == RES_OK);
-
- CHK(ssf_specular_transmission_setup(NULL, -1, -1) == RES_BAD_ARG);
- CHK(ssf_specular_transmission_setup(btdf, -1, -1) == RES_BAD_ARG);
- CHK(ssf_specular_transmission_setup(NULL, 1.00027, -1) == RES_BAD_ARG);
- CHK(ssf_specular_transmission_setup(btdf, 1.00027, -1) == RES_BAD_ARG);
- CHK(ssf_specular_transmission_setup(NULL, -1, 1.5) == RES_BAD_ARG);
- CHK(ssf_specular_transmission_setup(btdf, -1, 1.5) == RES_BAD_ARG);
- CHK(ssf_specular_transmission_setup(NULL, 1.00027, 1.5) == RES_BAD_ARG);
- CHK(ssf_specular_transmission_setup(btdf, 1.00027, 1.5) == RES_OK);
- CHK(ssf_specular_transmission_setup(dummy, 1.00027, 1.5) == RES_BAD_ARG);
-
- d3(N, 0.0, 1.0, 0.0);
- d3_normalize(wo, d3(wo, 1.0, 1.0, 0.0));
- R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &type, &pdf);
- CHK(eq_eps(R, ssf_fresnel_eval(fresnel, fabs(d3_dot(N, wi))), 1.e-6) == RES_OK);
- CHK(d3_eq_eps(wi, d3_minus(tmp, wo), 1.e-6) == 0);
- CHK(wi[1] < 0 == 1);
- CHK(IS_INF(pdf) == 1);
- CHK(type == SSF_SPECULAR|SSF_TRANSMISSION);
-
- CHK(ssf_bxdf_eval(btdf, wo, N, wi) == 0.0);
- CHK(ssf_bxdf_pdf(btdf, wo, N, wi) == 0.0);
-
- d3(wo, 0.0, 1.0, 0.0);
- R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &type, &pdf);
- CHK(eq_eps(R, ssf_fresnel_eval(fresnel, fabs(d3_dot(N, wi))), 1.e-6) == RES_OK);
- CHK(IS_INF(pdf) == 1);
- CHK(d3_eq_eps(wi, d3_minus(tmp, wo), 1.e-6) == 1);
- CHK(type == SSF_SPECULAR|SSF_TRANSMISSION);
-
- d3_normalize(wo, d3(wo, -1.0, 1.0, 0.0));
- R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &type, &pdf);
- reflect(tmp, wi, N);
- CHK(type == SSF_SPECULAR|SSF_TRANSMISSION);
- d3_normalize(wo, d3(wo, 1.0, 1.0, 0.0));
- R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &type, &pdf);
- CHK(d3_eq_eps(wi, tmp, 1.e-6) == 1);
- CHK(type == SSF_SPECULAR|SSF_TRANSMISSION);
-
- d3_normalize(wo, d3(wo, -1, 1.e-6, 0));
- R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &type, &pdf);
- CHK(eq_eps(R, 0, 1.e-3) == 1);
- CHK(type == SSF_SPECULAR|SSF_TRANSMISSION);
-
- /* Check total internal reflection */
- CHK(ssf_specular_transmission_setup(btdf, 1.5, 1.00027) == RES_OK);
- R = ssf_bxdf_sample(btdf, rng, wo, N, wi, &type, &pdf);
- CHK(eq_eps(R, 0, 1.e-3) == 1);
- CHK(type == SSF_SPECULAR|SSF_REFLECTION);
-
- CHK(ssf_fresnel_ref_put(fresnel) == RES_OK);
- CHK(ssf_bxdf_ref_put(btdf) == RES_OK);
- CHK(ssf_bxdf_ref_put(dummy) == RES_OK);
- CHK(ssp_rng_ref_put(rng) == RES_OK);
-
- check_memory_allocator(&allocator);
- mem_shutdown_proxy_allocator(&allocator);
- CHK(mem_allocated_size() == 0);
- return 0;
-}
diff --git a/src/test_ssf_thin_specular_dielectric.c b/src/test_ssf_thin_specular_dielectric.c
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -115,6 +115,15 @@ main(int argc, char** argv)
CHK(IS_INF(pdf) == 1);
CHK(d3_eq_eps(wi, d3(tmp, -wo[0], wo[1], 0), 1.e-6) == 1);
CHK(type == (SSF_SPECULAR | SSF_REFLECTION));
+
+ /* Check optional arguments */
+ pdf = 0, type = 0;
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, &pdf) == R);
+ CHK(IS_INF(pdf));
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, NULL) == R);
+ CHK(type == (SSF_SPECULAR | SSF_REFLECTION));
+ CHK(ssf_bsdf_sample(bsdf, rng, wo, N, wi, NULL, NULL) == R);
+
FOR_EACH(i, 0, NSTEPS) {
R = ssf_bsdf_sample(bsdf, rng, wo, N, wi, &type, &pdf);
CHK(type & SSF_REFLECTION);
diff --git a/src/test_ssf_utils.h b/src/test_ssf_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com)
+/* Copyright (C) 2016-2018 |Meso|Star> (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
@@ -178,6 +178,62 @@ static const struct ssf_microfacet_distribution_type microfacet_dummy = {
};
/*******************************************************************************
+ * Dummy Phase function type
+ ******************************************************************************/
+static res_T
+phase_dummy_init(struct mem_allocator* allocator, void* phase)
+{
+ (void)allocator, (void)phase;
+ return RES_OK;
+}
+
+static void
+phase_dummy_release(void* phase)
+{
+ (void)phase;
+}
+
+static void
+phase_dummy_sample
+ (void* phase,
+ struct ssp_rng* rng,
+ const double w[3],
+ double dir[3],
+ double* pdf)
+{
+ (void)phase, (void)rng, (void)w, (void)dir, (void)pdf;
+}
+
+static double
+phase_dummy_eval
+ (void* phase,
+ const double wo[3],
+ const double wi[3])
+{
+ (void)phase, (void)wo, (void)wi;
+ return 0.0;
+}
+
+static double
+phase_dummy_pdf
+ (void* phase,
+ const double wo[3],
+ const double wi[3])
+{
+ (void)phase, (void)wo, (void)wi;
+ return 0.0;
+}
+
+static const struct ssf_phase_type phase_dummy = {
+ phase_dummy_init,
+ phase_dummy_release,
+ phase_dummy_sample,
+ phase_dummy_eval,
+ phase_dummy_pdf,
+ 0, 1
+};
+
+/*******************************************************************************
* Miscellaneous functions
******************************************************************************/
static INLINE void
@@ -226,6 +282,9 @@ check_microfacet_distribution
D = ssf_microfacet_distribution_eval(distrib, N, wh);
weight = D * d3_dot(wh, N) / pdf;
CHK(eq_eps(weight, 1.0, 1.e-6) == 1);
+ CHK(eq_eps(pdf, ssf_microfacet_distribution_pdf(distrib, N, wh), 1.e-6f));
+
+ ssf_microfacet_distribution_sample(distrib, rng, N, wh, NULL);
}
}