commit 232c959b557d644334ca5e85d9246f341e9e6605
parent 8bbe0f6e6e8682e788633e71db304d2ce4dcc034
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Mon, 11 Dec 2017 10:38:18 +0100
Add and test the Pillbox microfacet distribution
Diffstat:
4 files changed, 212 insertions(+), 1 deletion(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -62,6 +62,7 @@ set(SSF_FILES_SRC
ssf_lambertian_reflection.c
ssf_microfacet_distribution.c
ssf_microfacet_reflection.c
+ ssf_pillbox_distribution.c
ssf_specular_dielectric_dielectric_interface.c
ssf_specular_reflection.c
ssf_thin_specular_dielectric.c)
@@ -101,6 +102,7 @@ if(NOT NO_TEST)
new_test(test_ssf_beckmann_distribution)
new_test(test_ssf_blinn_distribution)
+ new_test(test_ssf_pillbox_distribution)
new_test(test_ssf_bsdf)
new_test(test_ssf_fresnel)
new_test(test_ssf_fresnel_constant)
diff --git a/src/ssf.h b/src/ssf.h
@@ -255,6 +255,10 @@ SSF_API const struct ssf_microfacet_distribution_type ssf_beckmann_distribution;
* with `e' an exponent in [0, SSF_BLINN_DISTRIBUTION_MAX_EXPONENT] */
SSF_API const struct ssf_microfacet_distribution_type ssf_blinn_distribution;
+/* Pillbox microfacet distribution.
+ * D(wh) = */
+SSF_API const struct ssf_microfacet_distribution_type ssf_pillbox_distribution;
+
/*******************************************************************************
* BSDF API - Bidirectional Scattering Distribution Function. Describes the way
* the light is scattered by a surface. Note that by convention the outgoing
@@ -438,13 +442,18 @@ ssf_microfacet_distribution_get_data
SSF_API res_T
ssf_beckmann_distribution_setup
(struct ssf_microfacet_distribution* distrib,
- const double roughness); /* Must be > 0 */
+ const double roughness); /* In ]0, 1] */
SSF_API res_T
ssf_blinn_distribution_setup
(struct ssf_microfacet_distribution* distrib,
const double exponent); /* in [0, SSF_BLINN_DISTRIBUTION_MAX_EXPONENT] */
+SSF_API res_T
+ssf_pillbox_distribution_setup
+ (struct ssf_microfacet_distribution* distrib,
+ const double roughness); /* In ]0, 1] */
+
END_DECLS
#endif /* SSF_H */
diff --git a/src/ssf_pillbox_distribution.c b/src/ssf_pillbox_distribution.c
@@ -0,0 +1,132 @@
+/* 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_microfacet_distribution_c.h"
+
+#include <rsys/double3.h>
+#include <rsys/double33.h>
+
+#include <star/ssp.h>
+
+struct pillbox_distribution {
+ double sin2_theta_max;
+};
+
+/*******************************************************************************
+ * Private functions
+ ******************************************************************************/
+static res_T
+pillbox_distribution_init(struct mem_allocator* allocator, void* distrib)
+{
+ ASSERT(distrib);
+ (void)allocator;
+ ((struct pillbox_distribution*)distrib)->sin2_theta_max = 1.0;
+ return RES_OK;
+}
+
+static void
+pillbox_distribution_release(void* distrib)
+{ (void)distrib; }
+
+static double
+pillbox_distribution_eval(void* distrib, const double N[3], const double wh[3])
+{
+ struct pillbox_distribution* pillbox = distrib;
+ double cos2_theta_max;
+ double cos2_wh_N;
+ ASSERT(distrib && N && wh);
+ ASSERT(d3_is_normalized(wh) && d3_is_normalized(N));
+ cos2_wh_N = d3_dot(N, wh);
+ cos2_wh_N *= cos2_wh_N;
+ cos2_theta_max = 1.0 - pillbox->sin2_theta_max;
+ /* if |wh.N| >= theta_max then 0
+ * <=> if cos(|wh.N|)^2 < cos(theta_max)^2 then 0 */
+ return (cos2_wh_N >= cos2_theta_max) ? 1.0 / (PI - PI*cos2_theta_max) : 0.0;
+}
+
+static void
+pillbox_distribution_sample
+ (void* distrib,
+ struct ssp_rng* rng,
+ const double N[3],
+ double wh[3],
+ double* pdf)
+{
+ struct pillbox_distribution* pillbox = distrib;
+ double basis[9];
+ double dir[3];
+ double cos2_theta_max;
+ double phi, sin2_theta, sin_theta, cos_theta;
+ ASSERT(rng && wh && N);
+ ASSERT(d3_is_normalized(N));
+
+ cos2_theta_max = 1 - pillbox->sin2_theta_max;
+ sin2_theta = ssp_rng_uniform_double(rng, 0, pillbox->sin2_theta_max);
+ sin_theta = sqrt(sin2_theta);
+ cos_theta = sqrt(1 - sin2_theta);
+ phi = ssp_rng_uniform_double(rng, 0, 2 * PI);
+ dir[0] = cos(phi) * sin_theta;
+ 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);
+}
+
+static double
+pillbox_distribution_pdf(void* distrib, const double N[3], const double wh[3])
+{
+ struct pillbox_distribution* pillbox = distrib;
+ double cos_wh_N;
+ double cos2_wh_N;
+ double cos2_theta_max;
+ ASSERT(distrib && N && wh);
+ ASSERT(d3_is_normalized(wh) && d3_is_normalized(N));
+ cos_wh_N = d3_dot(wh, N);
+ if(cos_wh_N < 0.0) return 0.0;
+ cos2_theta_max = 1 - pillbox->sin2_theta_max;
+ cos2_wh_N = cos_wh_N * cos_wh_N;
+ if(cos2_wh_N < cos2_theta_max) return 0.0;
+ return cos_wh_N / (PI-PI*cos2_theta_max);
+}
+
+/*******************************************************************************
+ * Exported symbols
+ ******************************************************************************/
+const struct ssf_microfacet_distribution_type ssf_pillbox_distribution = {
+ pillbox_distribution_init,
+ pillbox_distribution_release,
+ pillbox_distribution_sample,
+ pillbox_distribution_eval,
+ pillbox_distribution_pdf,
+ sizeof(struct pillbox_distribution),
+ ALIGNOF(struct pillbox_distribution)
+};
+
+res_T
+ssf_pillbox_distribution_setup
+ (struct ssf_microfacet_distribution* distrib,
+ const double roughness)
+{
+ double sin2_theta_max;
+ if(!distrib || roughness <= 0 || roughness > 1) return RES_BAD_ARG;
+ if(!MICROFACET_DISTRIBUTION_TYPE_EQ(&distrib->type, &ssf_pillbox_distribution))
+ return RES_BAD_ARG;
+ sin2_theta_max = sin(roughness);
+ sin2_theta_max *= sin2_theta_max;
+ ((struct pillbox_distribution*)distrib->data)->sin2_theta_max = sin2_theta_max;
+ return RES_OK;
+}
+
diff --git a/src/test_ssf_pillbox_distribution.c b/src/test_ssf_pillbox_distribution.c
@@ -0,0 +1,68 @@
+/* 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/>. */
+
+#define _POSIX_C_SOURCE 200112L /* nextafter support */
+
+#include "ssf.h"
+#include "test_ssf_utils.h"
+
+#include <math.h>
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct ssp_rng* rng;
+ struct ssf_microfacet_distribution* distrib;
+ struct ssf_microfacet_distribution* dummy;
+ const size_t NTESTS = 10;
+ size_t itest;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+ CHECK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK);
+
+ CHECK(ssf_microfacet_distribution_create
+ (&allocator, &ssf_pillbox_distribution, &distrib), RES_OK);
+ CHECK(ssf_microfacet_distribution_create
+ (&allocator, µfacet_dummy, &dummy), RES_OK);
+
+ CHECK(ssf_pillbox_distribution_setup(NULL, -1), RES_BAD_ARG);
+ CHECK(ssf_pillbox_distribution_setup(distrib, -1), RES_BAD_ARG);
+ CHECK(ssf_pillbox_distribution_setup(NULL, 0.5), RES_BAD_ARG);
+ CHECK(ssf_pillbox_distribution_setup(distrib, 0.5), RES_OK);
+ CHECK(ssf_pillbox_distribution_setup(dummy, 0.5), RES_BAD_ARG);
+
+ CHECK(ssf_pillbox_distribution_setup(distrib, 1), RES_OK);
+ CHECK(ssf_pillbox_distribution_setup(distrib, nextafter(0, 1)), RES_OK);
+ CHECK(ssf_pillbox_distribution_setup(distrib, nextafter(1, 2)), RES_BAD_ARG);
+ CHECK(ssf_pillbox_distribution_setup(distrib, 0), RES_BAD_ARG);
+
+ FOR_EACH(itest, 0, NTESTS) {
+ const double roughness = nextafter(ssp_rng_canonical(rng), 2); /*in ]0, 1]*/
+ CHECK(ssf_pillbox_distribution_setup(distrib, roughness), RES_OK);
+ check_microfacet_distribution(distrib, rng);
+ }
+
+ CHECK(ssf_microfacet_distribution_ref_put(distrib), RES_OK);
+ CHECK(ssf_microfacet_distribution_ref_put(dummy), RES_OK);
+ CHECK(ssp_rng_ref_put(rng), RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHECK(mem_allocated_size(), 0);
+ return 0;
+}
+