star-sf

Set of surface and volume scattering functions
git clone git://git.meso-star.fr/star-sf.git
Log | Files | Refs | README | LICENSE

commit 58cd16d3d89bda2ab3e2f45c89075bdc45dc3bdf
parent ef1442447e44956317b98aa4d1fb5df702c42edb
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 30 Sep 2016 14:21:28 +0200

Test the Blinn microfacet distribution

Push further the tests on the Beckmann microfacet distribution.

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/ssf.h | 6+++++-
Msrc/ssf_beckmann_distribution.c | 2+-
Msrc/ssf_blinn_distribution.c | 7+++----
Msrc/test_ssf_beckmann_distribution.c | 49++++++++++++++-----------------------------------
Asrc/test_ssf_blinn_distribution.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_ssf_utils.h | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 165 insertions(+), 41 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -98,6 +98,7 @@ if(NOT NO_TEST) endfunction() new_test(test_ssf_beckmann_distribution) + new_test(test_ssf_blinn_distribution) new_test(test_ssf_bsdf) new_test(test_ssf_bxdf) new_test(test_ssf_fresnel) diff --git a/src/ssf.h b/src/ssf.h @@ -35,6 +35,10 @@ #define SSF(Func) ssf_##Func #endif +/* Maximum value of the exponent parameter in the the Blinn microfacet + * distribution */ +#define SSF_BLINN_DISTRIBUTION_MAX_EXPONENT 10000.0 + /* Forward declaration */ struct mem_allocator; @@ -403,7 +407,7 @@ ssf_beckmann_distribution_setup SSF_API res_T ssf_blinn_distribution_setup (struct ssf_microfacet_distribution* distrib, - const double exponent); /* in [0, 10000] */ + const double exponent); /* in [0, SSF_BLINN_DISTRIBUTION_MAX_EXPONENT] */ END_DECLS diff --git a/src/ssf_beckmann_distribution.c b/src/ssf_beckmann_distribution.c @@ -134,7 +134,7 @@ ssf_beckmann_distribution_setup (struct ssf_microfacet_distribution* distrib, const double roughness) { - if(!distrib || roughness <= 0) return RES_BAD_ARG; + if(!distrib || roughness <= 0 || roughness > 1) return RES_BAD_ARG; if(!MICROFACET_DISTRIBUTION_TYPE_EQ(&distrib->type, &ssf_beckmann_distribution)) return RES_BAD_ARG; ((struct beckmann_distribution*)distrib->data)->roughness = roughness; diff --git a/src/ssf_blinn_distribution.c b/src/ssf_blinn_distribution.c @@ -19,8 +19,6 @@ #include <rsys/double3.h> #include <rsys/double33.h> -#define EXPONENT_MAX 10000 - struct blinn_distribution { double exponent; }; @@ -49,7 +47,8 @@ blinn_distribution_eval double cos_wh_N; ASSERT(distrib && wo && N && wh); ASSERT(d3_is_normalized(wo) && d3_is_normalized(N) && d3_is_normalized(wh)); - ASSERT(blinn->exponent >= 0.0 && blinn->exponent <= EXPONENT_MAX); + ASSERT(blinn->exponent >= 0.0); + ASSERT(blinn->exponent <= SSF_BLINN_DISTRIBUTION_MAX_EXPONENT); (void)wo; cos_wh_N = d3_dot(wh, N); return (blinn->exponent + 2) / (2*PI) * pow(cos_wh_N, blinn->exponent); @@ -122,7 +121,7 @@ ssf_blinn_distribution_setup (struct ssf_microfacet_distribution* distrib, const double exponent) { - if(!distrib || exponent < 0 || exponent > EXPONENT_MAX) + if(!distrib || exponent < 0 || exponent > SSF_BLINN_DISTRIBUTION_MAX_EXPONENT) return RES_BAD_ARG; if(!MICROFACET_DISTRIBUTION_TYPE_EQ(&distrib->type, &ssf_blinn_distribution)) return RES_BAD_ARG; diff --git a/src/test_ssf_beckmann_distribution.c b/src/test_ssf_beckmann_distribution.c @@ -13,20 +13,21 @@ * 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 ssf_microfacet_distribution* distrib; struct ssf_microfacet_distribution* dummy; - double N[3], wo[3]; - double sum, sum_sqr; - double E, V, SE; - const size_t NSTEPS = 10000; - size_t i; + const size_t NTESTS = 10; + size_t itest; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -42,37 +43,15 @@ main(int argc, char** argv) CHECK(ssf_beckmann_distribution_setup(distrib, 0.5), RES_OK); CHECK(ssf_beckmann_distribution_setup(dummy, 0.5), RES_BAD_ARG); - /* Check that D(wh) is normalized wrt \int_{2PI} D(wh) |wh.n| dwh */ - d3(N, 0, 0, 1); - ran_hemisphere_cos(N, wo, NULL); - sum = sum_sqr = 0; - FOR_EACH(i, 0, NSTEPS) { - double wh[3], pdf; - double D, weight; - ran_hemisphere_cos(N, wh, &pdf); - D = ssf_microfacet_distribution_eval(distrib, wo, N, wh); - weight = D * d3_dot(wh, N) / pdf; - sum += weight; - sum_sqr += weight*weight; - - } - E = sum / (double)NSTEPS; - V = sum_sqr / (double)NSTEPS - E*E; - SE = sqrt(V/(double)NSTEPS); - CHECK(eq_eps(E, 1.0, SE), 1); - - /* Check the sampling of a direction wh and the returned pdf */ - sum = sum_sqr = 0; - FOR_EACH(i, 0, NSTEPS) { - const double u = rand_canonic(); - const double v = rand_canonic(); - double wh[3], pdf; - double D, weight; + CHECK(ssf_beckmann_distribution_setup(distrib, 1), RES_OK); + CHECK(ssf_beckmann_distribution_setup(distrib, nextafter(0, 1)), RES_OK); + CHECK(ssf_beckmann_distribution_setup(distrib, nextafter(1, 2)), RES_BAD_ARG); + CHECK(ssf_beckmann_distribution_setup(distrib, 0), RES_BAD_ARG); - ssf_microfacet_distribution_sample(distrib, u, v, wo, N, wh, &pdf); - D = ssf_microfacet_distribution_eval(distrib, wo, N, wh); - weight = D * d3_dot(wh, N) / pdf; - CHECK(eq_eps(weight, 1, 1.e-6), 1); + FOR_EACH(itest, 0, NTESTS) { + const double roughness = nextafter(rand_canonic(), 2); /* in ]0, 1] */ + CHECK(ssf_beckmann_distribution_setup(distrib, roughness), RES_OK); + check_microfacet_distribution(distrib); } CHECK(ssf_microfacet_distribution_ref_put(distrib), RES_OK); diff --git a/src/test_ssf_blinn_distribution.c b/src/test_ssf_blinn_distribution.c @@ -0,0 +1,66 @@ +/* Copyright (C) |Meso|Star> 2016 (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" + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + 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(ssf_microfacet_distribution_create + (&allocator, &ssf_blinn_distribution, &distrib), RES_OK); + CHECK(ssf_microfacet_distribution_create + (&allocator, &microfacet_dummy, &dummy), RES_OK); + + CHECK(ssf_blinn_distribution_setup(NULL, -1), RES_BAD_ARG); + CHECK(ssf_blinn_distribution_setup(distrib, -1), RES_BAD_ARG); + CHECK(ssf_blinn_distribution_setup(NULL, 8), RES_BAD_ARG); + CHECK(ssf_blinn_distribution_setup(distrib, 8), RES_OK); + CHECK(ssf_blinn_distribution_setup(distrib, 0), RES_OK); + CHECK(ssf_blinn_distribution_setup(dummy, 0), RES_BAD_ARG); + + CHECK(ssf_blinn_distribution_setup + (distrib, SSF_BLINN_DISTRIBUTION_MAX_EXPONENT), RES_OK); + CHECK(ssf_blinn_distribution_setup + (distrib, nextafter(SSF_BLINN_DISTRIBUTION_MAX_EXPONENT, DBL_MAX)), + RES_BAD_ARG); + CHECK(ssf_blinn_distribution_setup(distrib, nextafter(0,-1)), RES_BAD_ARG); + CHECK(ssf_blinn_distribution_setup(distrib, 32.32), RES_OK); + + FOR_EACH(itest, 0, NTESTS) { + const double exponent = rand_canonic()*SSF_BLINN_DISTRIBUTION_MAX_EXPONENT; + CHECK(ssf_blinn_distribution_setup(distrib, exponent), RES_OK); + check_microfacet_distribution(distrib); + } + + CHECK(ssf_microfacet_distribution_ref_put(distrib), RES_OK); + CHECK(ssf_microfacet_distribution_ref_put(dummy), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +} diff --git a/src/test_ssf_utils.h b/src/test_ssf_utils.h @@ -212,6 +212,81 @@ ran_hemisphere_cos return dir; } +static INLINE double* +ran_hemisphere(const double N[3], double dir[3], double *pdf) +{ + const double u = rand_canonic(); + const double v = rand_canonic(); + const double phi = 2.0 * PI * u; + const double cos_theta = v; + const double sin_theta = cos2sin(cos_theta); + double basis[9]; + double tmp[3]; + tmp[0] = cos(phi) * sin_theta; + tmp[1] = sin(phi) * sin_theta; + tmp[2] = cos_theta; + d33_muld3(dir, d33_basis(basis, N), tmp); + if(pdf) *pdf = 1/(2*PI); + return dir; +} + +static INLINE void +check_microfacet_distribution + (struct ssf_microfacet_distribution* distrib) +{ + double N[3]; + const size_t NSTEPS = 10000; + size_t i; + size_t n; + double wo[3]; + double E, V, SE; + double sum, sum2; + + N[0] = rand_canonic()*2 - 1; + N[1] = rand_canonic()*2 - 1; + N[2] = rand_canonic()*2 - 1; + d3_normalize(N, N); + + ran_hemisphere(N, wo, NULL); + + /* Check that D(wh) is normalized wrt \int_{2PI} D(wh) |wh.n| dwh */ + sum = sum2 = 0; + n = 0; + do { + FOR_EACH(i, 0, NSTEPS) { + double wh[3], pdf, D, weight; + ran_hemisphere_cos(N, wh, &pdf); + D = ssf_microfacet_distribution_eval(distrib, wo, N, wh); + weight = D * d3_dot(wh, N) / pdf; + sum += weight; + sum2 += weight*weight; + } + n += NSTEPS; + E = sum / (double)n; + V = MMAX(sum2 / (double)n - E*E, 0); + SE = sqrt(V/(double)n); + } while(SE > E * 0.05); + CHECK(eq_eps(E, 1.0, 3*SE), 1); + + /* Check the sampling of a direction wh and the returned pdf */ + FOR_EACH(i, 0, NSTEPS) { + const double u = rand_canonic(); + const double v = rand_canonic(); + double wh[3], pdf, D, weight; + + ssf_microfacet_distribution_sample(distrib, u, v, wo, N, wh, &pdf); + D = ssf_microfacet_distribution_eval(distrib, wo, N, wh); + weight = D * d3_dot(wh, N) / pdf; + sum += weight; + sum2 += weight*weight; + } + n += NSTEPS; + E = sum / (double)n; + V = MMAX(sum2 / (double)n - E*E, 0); + SE = sqrt(V/(double)n); + CHECK(eq_eps(E, 1.0, 3*SE), 1); +} + static INLINE void check_memory_allocator(struct mem_allocator* allocator) {