commit b0517721d55aff895bdb70982d063b54aa7976d6
parent fa4d4d70e6a09d152f4d4d18b80cb05eb022e86c
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Fri, 20 Jul 2018 11:53:50 +0200
Implement the built-in Rayleigh phase function
Diffstat:
4 files changed, 136 insertions(+), 22 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -65,6 +65,7 @@ set(SSF_FILES_SRC
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
diff --git a/src/ssf.h b/src/ssf.h
@@ -150,8 +150,8 @@ 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 */
+ * 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 */
diff --git a/src/ssf_phase_hg.c b/src/ssf_phase_hg.c
@@ -34,11 +34,19 @@ hg_init(struct mem_allocator* allocator, void* phase)
return RES_OK;
}
-static void
-hg_release(void* phase)
-{ (void)phase; }
+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 */
+/* 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])
{
@@ -47,18 +55,21 @@ hg_eval(void* data, const double wo[3], const double wi[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 point outward the scattering point. Revert it to point
+ /* 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);
- return 1.0/(4.0*PI) * (1 - g*g) / (denom*sqrt(denom));
+ 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
@@ -70,21 +81,12 @@ hg_sample
double* pdf)
{
const struct hg* hg = data;
- double sample[3];
+ double w[3];
ASSERT(data && wo && wi);
- ssp_ran_sphere_hg(rng, wo, hg->g, sample, pdf);
- d3_set(wi, sample);
-}
-static double
-hg_pdf
- (void* data,
- const double wo[3],
- const double wi[3])
-{
- const struct hg* hg = data;
- ASSERT(data && wo && wi);
- return ssp_ran_sphere_hg_pdf(wo, hg->g, wi);
+ /* 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);
}
/*******************************************************************************
@@ -92,7 +94,7 @@ hg_pdf
******************************************************************************/
const struct ssf_phase_type ssf_phase_hg = {
hg_init,
- hg_release,
+ NULL,
hg_sample,
hg_eval,
hg_pdf,
diff --git a/src/ssf_phase_rayleigh.c b/src/ssf_phase_rayleigh.c
@@ -0,0 +1,111 @@
+/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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
+};
+